# newgrf_text.cpp

Go to the documentation of this file.

    00001 /* $Id: newgrf_text.cpp 26715 2014-08-03 14:06:04Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00020 #include "stdafx.h"
    00021 #include "newgrf.h"
    00022 #include "strings_func.h"
    00023 #include "newgrf_storage.h"
    00024 #include "newgrf_text.h"
    00025 #include "newgrf_cargo.h"
    00026 #include "string_func.h"
    00027 #include "date_type.h"
    00028 #include "debug.h"
    00029 #include "core/alloc_type.hpp"
    00030 #include "core/smallmap_type.hpp"
    00031 #include "language.h"
    00032
    00033 #include "table/strings.h"
    00034 #include "table/control_codes.h"
    00035
    00036 #define GRFTAB  28
    00037 #define TABSIZE 11
    00038
    00044 enum GRFBaseLanguages {
    00045   GRFLB_AMERICAN    = 0x01,
    00046   GRFLB_ENGLISH     = 0x02,
    00047   GRFLB_GERMAN      = 0x04,
    00048   GRFLB_FRENCH      = 0x08,
    00049   GRFLB_SPANISH     = 0x10,
    00050   GRFLB_GENERIC     = 0x80,
    00051 };
    00052
    00053 enum GRFExtendedLanguages {
    00054   GRFLX_AMERICAN    = 0x00,
    00055   GRFLX_ENGLISH     = 0x01,
    00056   GRFLX_GERMAN      = 0x02,
    00057   GRFLX_FRENCH      = 0x03,
    00058   GRFLX_SPANISH     = 0x04,
    00059   GRFLX_UNSPECIFIED = 0x7F,
    00060 };
    00061
    00067 struct GRFText {
    00068 public:
    00079   static GRFText *New(byte langid, const char *text, size_t len)
    00080   {
    00081     return new (len) GRFText(langid, text, len);
    00082   }
    00083
    00089   static GRFText *Copy(GRFText *orig)
    00090   {
    00091     return GRFText::New(orig->langid, orig->text, orig->len);
    00092   }
    00093
    00099   void *operator new(size_t size)
    00100   {
    00101     NOT_REACHED();
    00102   }
    00103
    00108   void operator delete(void *p)
    00109   {
    00110     free(p);
    00111   }
    00112 private:
    00119   GRFText(byte langid_, const char *text_, size_t len_) : next(NULL), len(len_), langid(langid_)
    00120   {
    00121     /* We need to use memcpy instead of strcpy due to
    00122      * the possibility of "choice lists" and therefore
    00123      * intermediate string terminators. */
    00124     memcpy(this->text, text_, len);
    00125   }
    00126
    00133   void *operator new(size_t size, size_t extra)
    00134   {
    00135     return MallocT<byte>(size + extra);
    00136   }
    00137
    00138 public:
    00139   GRFText *next;
    00140   size_t len;
    00141   byte langid;
    00142   char text[];
    00143 };
    00144
    00145
    00151 struct GRFTextEntry {
    00152   uint32 grfid;
    00153   uint16 stringid;
    00154   StringID def_string;
    00155   GRFText *textholder;
    00156 };
    00157
    00158
    00159 static uint _num_grf_texts = 0;
    00160 static GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
    00161 static byte _currentLangID = GRFLX_ENGLISH;
    00162
    00169 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
    00170 {
    00171   const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
    00172   for (const Mapping *m = map.Begin(); m != map.End(); m++) {
    00173     if (m->newgrf_id == newgrf_id) return m->openttd_id;
    00174   }
    00175   return -1;
    00176 }
    00177
    00184 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
    00185 {
    00186   const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
    00187   for (const Mapping *m = map.Begin(); m != map.End(); m++) {
    00188     if (m->openttd_id == openttd_id) return m->newgrf_id;
    00189   }
    00190   return -1;
    00191 }
    00192
    00194 struct UnmappedChoiceList : ZeroedMemoryAllocator {
    00196   ~UnmappedChoiceList()
    00197   {
    00198     for (SmallPair<byte, char *> *p = this->strings.Begin(); p < this->strings.End(); p++) {
    00199       free(p->second);
    00200     }
    00201   }
    00202
    00209   UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
    00210     type(type), old_d(old_d), offset(offset)
    00211   {
    00212   }
    00213
    00214   StringControlCode type;
    00215   char *old_d;
    00216   int offset;
    00217
    00219   SmallMap<byte, char *> strings;
    00220
    00226   char *Flush(const LanguageMap *lm)
    00227   {
    00228     if (!this->strings.Contains(0)) {
    00229       /* In case of a (broken) NewGRF without a default,
    00230        * assume an empty string. */
    00231       grfmsg(1, "choice list misses default value");
    00232       this->strings[0] = strdup("");
    00233     }
    00234
    00235     char *d = old_d;
    00236     if (lm == NULL) {
    00237       /* In case there is no mapping, just ignore everything but the default.
    00238        * A probable cause for this happening is when the language file has
    00239        * been removed by the user and as such no mapping could be made. */
    00240       size_t len = strlen(this->strings[0]);
    00241       memcpy(d, this->strings[0], len);
    00242       return d + len;
    00243     }
    00244
    00245     d += Utf8Encode(d, this->type);
    00246
    00247     if (this->type == SCC_SWITCH_CASE) {
    00248       /*
    00249        * Format for case switch:
    00250        * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
    00251        * Each LEN is printed using 2 bytes in big endian order.
    00252        */
    00253
    00254       /* "<NUM CASES>" */
    00255       int count = 0;
    00256       for (uint8 i = 0; i < _current_language->num_cases; i++) {
    00257         /* Count the ones we have a mapped string for. */
    00258         if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
    00259       }
    00260       *d++ = count;
    00261
    00262       for (uint8 i = 0; i < _current_language->num_cases; i++) {
    00263         /* Resolve the string we're looking for. */
    00264         int idx = lm->GetReverseMapping(i, false);
    00265         if (!this->strings.Contains(idx)) continue;
    00266         char *str = this->strings[idx];
    00267
    00268         /* "<CASEn>" */
    00269         *d++ = i + 1;
    00270
    00271         /* "<LENn>" */
    00272         size_t len = strlen(str) + 1;
    00273         *d++ = GB(len, 8, 8);
    00274         *d++ = GB(len, 0, 8);
    00275
    00276         /* "<STRINGn>" */
    00277         memcpy(d, str, len);
    00278         d += len;
    00279       }
    00280
    00281       /* "<STRINGDEFAULT>" */
    00282       size_t len = strlen(this->strings[0]) + 1;
    00283       memcpy(d, this->strings[0], len);
    00284       d += len;
    00285     } else {
    00286       if (this->type == SCC_PLURAL_LIST) {
    00287         *d++ = lm->plural_form;
    00288       }
    00289
    00290       /*
    00291        * Format for choice list:
    00292        * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
    00293        */
    00294
    00295       /* "<OFFSET>" */
    00296       *d++ = this->offset - 0x80;
    00297
    00298       /* "<NUM CHOICES>" */
    00299       int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
    00300       *d++ = count;
    00301
    00302       /* "<LENs>" */
    00303       for (int i = 0; i < count; i++) {
    00304         int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
    00305         const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
    00306         size_t len = strlen(str) + 1;
    00307         if (len > 0xFF) grfmsg(1, "choice list string is too long");
    00308         *d++ = GB(len, 0, 8);
    00309       }
    00310
    00311       /* "<STRINGs>" */
    00312       for (int i = 0; i < count; i++) {
    00313         int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
    00314         const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
    00315         /* Limit the length of the string we copy to 0xFE. The length is written above
    00316          * as a byte and we need room for the final '\0'. */
    00317         size_t len = min<size_t>(0xFE, strlen(str));
    00318         memcpy(d, str, len);
    00319         d += len;
    00320         *d++ = '\0';
    00321       }
    00322     }
    00323     return d;
    00324   }
    00325 };
    00326
    00337 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
    00338 {
    00339   char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
    00340   char *d = tmp;
    00341   bool unicode = false;
    00342   WChar c;
    00343   size_t len = Utf8Decode(&c, str);
    00344
    00345   /* Helper variable for a possible (string) mapping. */
    00346   UnmappedChoiceList *mapping = NULL;
    00347
    00348   if (c == NFO_UTF8_IDENTIFIER) {
    00349     unicode = true;
    00350     str += len;
    00351   }
    00352
    00353   for (;;) {
    00354     if (unicode && Utf8EncodedCharLen(*str) != 0) {
    00355       c = Utf8Consume(&str);
    00356       /* 'Magic' range of control codes. */
    00357       if (GB(c, 8, 8) == 0xE0) {
    00358         c = GB(c, 0, 8);
    00359       } else if (c >= 0x20) {
    00360         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
    00361         d += Utf8Encode(d, c);
    00362         continue;
    00363       }
    00364     } else {
    00365       c = (byte)*str++;
    00366     }
    00367     if (c == '\0') break;
    00368
    00369     switch (c) {
    00370       case 0x01:
    00371         if (str[0] == '\0') goto string_end;
    00372         d += Utf8Encode(d, ' ');
    00373         str++;
    00374         break;
    00375       case 0x0A: break;
    00376       case 0x0D:
    00377         if (allow_newlines) {
    00378           *d++ = 0x0A;
    00379         } else {
    00380           grfmsg(1, "Detected newline in string that does not allow one");
    00381         }
    00382         break;
    00383       case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
    00384       case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
    00385       case 0x1F:
    00386         if (str[0] == '\0' || str[1] == '\0') goto string_end;
    00387         d += Utf8Encode(d, ' ');
    00388         str += 2;
    00389         break;
    00390       case 0x7B:
    00391       case 0x7C:
    00392       case 0x7D:
    00393       case 0x7E:
    00394       case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
    00395       case 0x80: d += Utf8Encode(d, byte80); break;
    00396       case 0x81: {
    00397         if (str[0] == '\0' || str[1] == '\0') goto string_end;
    00398         StringID string;
    00399         string  = ((uint8)*str++);
    00400         string |= ((uint8)*str++) << 8;
    00401         d += Utf8Encode(d, SCC_NEWGRF_STRINL);
    00402         d += Utf8Encode(d, MapGRFStringID(grfid, string));
    00403         break;
    00404       }
    00405       case 0x82:
    00406       case 0x83:
    00407       case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
    00408       case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD);       break;
    00409       case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
    00410       case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG);  break;
    00411       case 0x88: d += Utf8Encode(d, SCC_BLUE);    break;
    00412       case 0x89: d += Utf8Encode(d, SCC_SILVER);  break;
    00413       case 0x8A: d += Utf8Encode(d, SCC_GOLD);    break;
    00414       case 0x8B: d += Utf8Encode(d, SCC_RED);     break;
    00415       case 0x8C: d += Utf8Encode(d, SCC_PURPLE);  break;
    00416       case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
    00417       case 0x8E: d += Utf8Encode(d, SCC_ORANGE);  break;
    00418       case 0x8F: d += Utf8Encode(d, SCC_GREEN);   break;
    00419       case 0x90: d += Utf8Encode(d, SCC_YELLOW);  break;
    00420       case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
    00421       case 0x92: d += Utf8Encode(d, SCC_CREAM);   break;
    00422       case 0x93: d += Utf8Encode(d, SCC_BROWN);   break;
    00423       case 0x94: d += Utf8Encode(d, SCC_WHITE);   break;
    00424       case 0x95: d += Utf8Encode(d, SCC_LTBLUE);  break;
    00425       case 0x96: d += Utf8Encode(d, SCC_GRAY);    break;
    00426       case 0x97: d += Utf8Encode(d, SCC_DKBLUE);  break;
    00427       case 0x98: d += Utf8Encode(d, SCC_BLACK);   break;
    00428       case 0x9A: {
    00429         int code = *str++;
    00430         switch (code) {
    00431           case 0x00: goto string_end;
    00432           case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
    00433           /* 0x02: ignore next colour byte is not supported. It works on the final
    00434            * string and as such hooks into the string drawing routine. At that
    00435            * point many things already happened, such as splitting up of strings
    00436            * when drawn over multiple lines or right-to-left translations, which
    00437            * make the behaviour peculiar, e.g. only happening at specific width
    00438            * of windows. Or we need to add another pass over the string to just
    00439            * support this. As such it is not implemented in OpenTTD. */
    00440           case 0x03: {
    00441             if (str[0] == '\0' || str[1] == '\0') goto string_end;
    00442             uint16 tmp  = ((uint8)*str++);
    00443             tmp        |= ((uint8)*str++) << 8;
    00444             d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD);
    00445             d += Utf8Encode(d, tmp);
    00446             break;
    00447           }
    00448           case 0x04:
    00449             if (str[0] == '\0') goto string_end;
    00450             d += Utf8Encode(d, SCC_NEWGRF_UNPRINT);
    00451             d += Utf8Encode(d, *str++);
    00452             break;
    00453           case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX);          break;
    00454           case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX);          break;
    00455           case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX);         break;
    00456           /* 0x09, 0x0A are TTDPatch internal use only string codes. */
    00457           case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX);         break;
    00458           case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
    00459           case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG);  break;
    00460           case 0x0E:
    00461           case 0x0F: {
    00462             if (str[0] == '\0') goto string_end;
    00463             const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
    00464             int index = *str++;
    00465             int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1;
    00466             if (mapped >= 0) {
    00467               d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
    00468               d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
    00469             }
    00470             break;
    00471           }
    00472
    00473           case 0x10:
    00474           case 0x11:
    00475             if (str[0] == '\0') goto string_end;
    00476             if (mapping == NULL) {
    00477               if (code == 0x10) str++; // Skip the index
    00478               grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
    00479               break;
    00480             } else {
    00481               /* Terminate the previous string. */
    00482               *d = '\0';
    00483               int index = (code == 0x10 ? *str++ : 0);
    00484               if (mapping->strings.Contains(index)) {
    00485                 grfmsg(1, "duplicate choice list string, ignoring");
    00486                 d++;
    00487               } else {
    00488                 d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
    00489               }
    00490             }
    00491             break;
    00492
    00493           case 0x12:
    00494             if (mapping == NULL) {
    00495               grfmsg(1, "choice list end marker found when not expected");
    00496             } else {
    00497               /* Terminate the previous string. */
    00498               *d = '\0';
    00499
    00500               /* Now we can start flushing everything and clean everything up. */
    00501               d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
    00502               delete mapping;
    00503               mapping = NULL;
    00504             }
    00505             break;
    00506
    00507           case 0x13:
    00508           case 0x14:
    00509           case 0x15:
    00510             if (str[0] == '\0') goto string_end;
    00511             if (mapping != NULL) {
    00512               grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
    00513               if (code != 0x14) str++;
    00514             } else {
    00515               static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
    00516               mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
    00517             }
    00518             break;
    00519
    00520           case 0x16:
    00521           case 0x17:
    00522           case 0x18:
    00523           case 0x19:
    00524           case 0x1A:
    00525           case 0x1B:
    00526           case 0x1C:
    00527           case 0x1D:
    00528             d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
    00529             break;
    00530
    00531           default:
    00532             grfmsg(1, "missing handler for extended format code");
    00533             break;
    00534         }
    00535         break;
    00536       }
    00537
    00538       case 0x9E: d += Utf8Encode(d, 0x20AC);               break; // Euro
    00539       case 0x9F: d += Utf8Encode(d, 0x0178);               break; // Y with diaeresis
    00540       case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW);         break;
    00541       case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW);       break;
    00542       case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK);        break;
    00543       case 0xAD: d += Utf8Encode(d, SCC_CROSS);            break;
    00544       case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW);      break;
    00545       case 0xB4: d += Utf8Encode(d, SCC_TRAIN);            break;
    00546       case 0xB5: d += Utf8Encode(d, SCC_LORRY);            break;
    00547       case 0xB6: d += Utf8Encode(d, SCC_BUS);              break;
    00548       case 0xB7: d += Utf8Encode(d, SCC_PLANE);            break;
    00549       case 0xB8: d += Utf8Encode(d, SCC_SHIP);             break;
    00550       case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1);   break;
    00551       case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW);   break;
    00552       case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
    00553       default:
    00554         /* Validate any unhandled character */
    00555         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
    00556         d += Utf8Encode(d, c);
    00557         break;
    00558     }
    00559   }
    00560
    00561 string_end:
    00562   if (mapping != NULL) {
    00563     grfmsg(1, "choice list was incomplete, the whole list is ignored");
    00564     delete mapping;
    00565   }
    00566
    00567   *d = '\0';
    00568   if (olen != NULL) *olen = d - tmp + 1;
    00569   tmp = ReallocT(tmp, d - tmp + 1);
    00570   return tmp;
    00571 }
    00572
    00578 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
    00579 {
    00580   GRFText **ptext, *text;
    00581
    00582   /* Loop through all languages and see if we can replace a string */
    00583   for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
    00584     if (text->langid == text_to_add->langid) {
    00585       text_to_add->next = text->next;
    00586       *ptext = text_to_add;
    00587       delete text;
    00588       return;
    00589     }
    00590   }
    00591
    00592   /* If a string wasn't replaced, then we must append the new string */
    00593   *ptext = text_to_add;
    00594 }
    00595
    00605 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
    00606 {
    00607   int len;
    00608   char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
    00609   GRFText *newtext = GRFText::New(langid, translatedtext, len);
    00610   free(translatedtext);
    00611
    00612   AddGRFTextToList(list, newtext);
    00613 }
    00614
    00621 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
    00622 {
    00623   AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
    00624 }
    00625
    00631 GRFText *DuplicateGRFText(GRFText *orig)
    00632 {
    00633   GRFText *newtext = NULL;
    00634   GRFText **ptext = &newtext;
    00635   for (; orig != NULL; orig = orig->next) {
    00636     *ptext = GRFText::Copy(orig);
    00637     ptext = &(*ptext)->next;
    00638   }
    00639   return newtext;
    00640 }
    00641
    00645 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
    00646 {
    00647   char *translatedtext;
    00648   uint id;
    00649
    00650   /* When working with the old language scheme (grf_version is less than 7) and
    00651    * English or American is among the set bits, simply add it as English in
    00652    * the new scheme, i.e. as langid = 1.
    00653    * If English is set, it is pretty safe to assume the translations are not
    00654    * actually translated.
    00655    */
    00656   if (!new_scheme) {
    00657     if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
    00658       langid_to_add = GRFLX_ENGLISH;
    00659     } else {
    00660       StringID ret = STR_EMPTY;
    00661       if (langid_to_add & GRFLB_GERMAN)  ret = AddGRFString(grfid, stringid, GRFLX_GERMAN,  true, allow_newlines, text_to_add, def_string);
    00662       if (langid_to_add & GRFLB_FRENCH)  ret = AddGRFString(grfid, stringid, GRFLX_FRENCH,  true, allow_newlines, text_to_add, def_string);
    00663       if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
    00664       return ret;
    00665     }
    00666   }
    00667
    00668   for (id = 0; id < _num_grf_texts; id++) {
    00669     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
    00670       break;
    00671     }
    00672   }
    00673
    00674   /* Too many strings allocated, return empty */
    00675   if (id == lengthof(_grf_text)) return STR_EMPTY;
    00676
    00677   int len;
    00678   translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
    00679
    00680   GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
    00681
    00682   free(translatedtext);
    00683
    00684   /* If we didn't find our stringid and grfid in the list, allocate a new id */
    00685   if (id == _num_grf_texts) _num_grf_texts++;
    00686
    00687   if (_grf_text[id].textholder == NULL) {
    00688     _grf_text[id].grfid      = grfid;
    00689     _grf_text[id].stringid   = stringid;
    00690     _grf_text[id].def_string = def_string;
    00691   }
    00692   AddGRFTextToList(&_grf_text[id].textholder, newtext);
    00693
    00694   grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
    00695
    00696   return (GRFTAB << TABSIZE) + id;
    00697 }
    00698
    00702 StringID GetGRFStringID(uint32 grfid, uint16 stringid)
    00703 {
    00704   for (uint id = 0; id < _num_grf_texts; id++) {
    00705     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
    00706       return (GRFTAB << TABSIZE) + id;
    00707     }
    00708   }
    00709
    00710   return STR_UNDEFINED;
    00711 }
    00712
    00713
    00721 const char *GetGRFStringFromGRFText(const GRFText *text)
    00722 {
    00723   const char *default_text = NULL;
    00724
    00725   /* Search the list of lang-strings of this stringid for current lang */
    00726   for (; text != NULL; text = text->next) {
    00727     if (text->langid == _currentLangID) return text->text;
    00728
    00729     /* If the current string is English or American, set it as the
    00730      * fallback language if the specific language isn't available. */
    00731     if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
    00732       default_text = text->text;
    00733     }
    00734   }
    00735
    00736   return default_text;
    00737 }
    00738
    00742 const char *GetGRFStringPtr(uint16 stringid)
    00743 {
    00744   assert(_grf_text[stringid].grfid != 0);
    00745
    00746   const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
    00747   if (str != NULL) return str;
    00748
    00749   /* Use the default string ID if the fallback string isn't available */
    00750   return GetStringPtr(_grf_text[stringid].def_string);
    00751 }
    00752
    00761 void SetCurrentGrfLangID(byte language_id)
    00762 {
    00763   _currentLangID = language_id;
    00764 }
    00765
    00766 bool CheckGrfLangID(byte lang_id, byte grf_version)
    00767 {
    00768   if (grf_version < 7) {
    00769     switch (_currentLangID) {
    00770       case GRFLX_GERMAN:  return (lang_id & GRFLB_GERMAN)  != 0;
    00771       case GRFLX_FRENCH:  return (lang_id & GRFLB_FRENCH)  != 0;
    00772       case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
    00773       default:            return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
    00774     }
    00775   }
    00776
    00777   return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
    00778 }
    00779
    00784 void CleanUpGRFText(GRFText *grftext)
    00785 {
    00786   while (grftext != NULL) {
    00787     GRFText *grftext2 = grftext->next;
    00788     delete grftext;
    00789     grftext = grftext2;
    00790   }
    00791 }
    00792
    00797 void CleanUpStrings()
    00798 {
    00799   uint id;
    00800
    00801   for (id = 0; id < _num_grf_texts; id++) {
    00802     CleanUpGRFText(_grf_text[id].textholder);
    00803     _grf_text[id].grfid      = 0;
    00804     _grf_text[id].stringid   = 0;
    00805     _grf_text[id].textholder = NULL;
    00806   }
    00807
    00808   _num_grf_texts = 0;
    00809 }
    00810
    00811 struct TextRefStack {
    00812   byte stack[0x30];
    00813   byte position;
    00814   const GRFFile *grffile;
    00815   bool used;
    00816
    00817   TextRefStack() : position(0), grffile(NULL), used(false) {}
    00818
    00819   TextRefStack(const TextRefStack &stack) :
    00820     position(stack.position),
    00821     grffile(stack.grffile),
    00822     used(stack.used)
    00823   {
    00824     memcpy(this->stack, stack.stack, sizeof(this->stack));
    00825   }
    00826
    00827   uint8  PopUnsignedByte()  { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
    00828   int8   PopSignedByte()    { return (int8)this->PopUnsignedByte(); }
    00829
    00830   uint16 PopUnsignedWord()
    00831   {
    00832     uint16 val = this->PopUnsignedByte();
    00833     return val | (this->PopUnsignedByte() << 8);
    00834   }
    00835   int16  PopSignedWord()    { return (int32)this->PopUnsignedWord(); }
    00836
    00837   uint32 PopUnsignedDWord()
    00838   {
    00839     uint32 val = this->PopUnsignedWord();
    00840     return val | (this->PopUnsignedWord() << 16);
    00841   }
    00842   int32  PopSignedDWord()   { return (int32)this->PopUnsignedDWord(); }
    00843
    00844   uint64 PopUnsignedQWord()
    00845   {
    00846     uint64 val = this->PopUnsignedDWord();
    00847     return val | (((uint64)this->PopUnsignedDWord()) << 32);
    00848   }
    00849   int64  PopSignedQWord()   { return (int64)this->PopUnsignedQWord(); }
    00850
    00852   void RotateTop4Words()
    00853   {
    00854     byte tmp[2];
    00855     for (int i = 0; i  < 2; i++) tmp[i] = this->stack[this->position + i + 6];
    00856     for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
    00857     for (int i = 0; i  < 2; i++) this->stack[this->position + i] = tmp[i];
    00858   }
    00859
    00860   void PushWord(uint16 word)
    00861   {
    00862     if (this->position >= 2) {
    00863       this->position -= 2;
    00864     } else {
    00865       for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
    00866         this->stack[i] = this->stack[i - 2];
    00867       }
    00868     }
    00869     this->stack[this->position]     = GB(word, 0, 8);
    00870     this->stack[this->position + 1] = GB(word, 8, 8);
    00871   }
    00872
    00873   void ResetStack(const GRFFile *grffile)
    00874   {
    00875     assert(grffile != NULL);
    00876     this->position = 0;
    00877     this->grffile = grffile;
    00878     this->used = true;
    00879   }
    00880
    00881   void RewindStack() { this->position = 0; }
    00882 };
    00883
    00885 static TextRefStack _newgrf_textrefstack;
    00886
    00891 bool UsingNewGRFTextStack()
    00892 {
    00893   return _newgrf_textrefstack.used;
    00894 }
    00895
    00900 struct TextRefStack *CreateTextRefStackBackup()
    00901 {
    00902   return new TextRefStack(_newgrf_textrefstack);
    00903 }
    00904
    00909 void RestoreTextRefStackBackup(struct TextRefStack *backup)
    00910 {
    00911   _newgrf_textrefstack = *backup;
    00912   delete backup;
    00913 }
    00914
    00933 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
    00934 {
    00935   extern TemporaryStorageArray<int32, 0x110> _temp_store;
    00936
    00937   _newgrf_textrefstack.ResetStack(grffile);
    00938
    00939   byte *p = _newgrf_textrefstack.stack;
    00940   for (uint i = 0; i < numEntries; i++) {
    00941     uint32 value = values != NULL ? values[i] : _temp_store.GetValue(0x100 + i);
    00942     for (uint j = 0; j < 32; j += 8) {
    00943       *p = GB(value, j, 8);
    00944       p++;
    00945     }
    00946   }
    00947 }
    00948
    00950 void StopTextRefStackUsage()
    00951 {
    00952   _newgrf_textrefstack.used = false;
    00953 }
    00954
    00955 void RewindTextRefStack()
    00956 {
    00957   _newgrf_textrefstack.RewindStack();
    00958 }
    00959
    00970 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
    00971 {
    00972   switch (scc) {
    00973     default: break;
    00974
    00975     case SCC_NEWGRF_PRINT_DWORD_SIGNED:
    00976     case SCC_NEWGRF_PRINT_WORD_SIGNED:
    00977     case SCC_NEWGRF_PRINT_BYTE_SIGNED:
    00978     case SCC_NEWGRF_PRINT_WORD_UNSIGNED:
    00979     case SCC_NEWGRF_PRINT_BYTE_HEX:
    00980     case SCC_NEWGRF_PRINT_WORD_HEX:
    00981     case SCC_NEWGRF_PRINT_DWORD_HEX:
    00982     case SCC_NEWGRF_PRINT_QWORD_HEX:
    00983     case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
    00984     case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
    00985     case SCC_NEWGRF_PRINT_WORD_STRING_ID:
    00986     case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
    00987     case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
    00988     case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:
    00989     case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
    00990     case SCC_NEWGRF_PRINT_WORD_SPEED:
    00991     case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
    00992     case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
    00993     case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
    00994     case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
    00995     case SCC_NEWGRF_PRINT_WORD_POWER:
    00996     case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
    00997       if (argv_size < 1) {
    00998         DEBUG(misc, 0, "Too many NewGRF string parameters.");
    00999         return 0;
    01000       }
    01001       break;
    01002
    01003     case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
    01004     case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
    01005     case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
    01006       if (argv_size < 2) {
    01007         DEBUG(misc, 0, "Too many NewGRF string parameters.");
    01008         return 0;
    01009       }
    01010       break;
    01011   }
    01012
    01013   if (_newgrf_textrefstack.used && modify_argv) {
    01014     switch (scc) {
    01015       default: NOT_REACHED();
    01016       case SCC_NEWGRF_PRINT_BYTE_SIGNED:      *argv = _newgrf_textrefstack.PopSignedByte();    break;
    01017       case SCC_NEWGRF_PRINT_QWORD_CURRENCY:   *argv = _newgrf_textrefstack.PopSignedQWord();   break;
    01018
    01019       case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
    01020       case SCC_NEWGRF_PRINT_DWORD_SIGNED:     *argv = _newgrf_textrefstack.PopSignedDWord();   break;
    01021
    01022       case SCC_NEWGRF_PRINT_BYTE_HEX:         *argv = _newgrf_textrefstack.PopUnsignedByte();  break;
    01023       case SCC_NEWGRF_PRINT_QWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
    01024
    01025       case SCC_NEWGRF_PRINT_WORD_SPEED:
    01026       case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
    01027       case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
    01028       case SCC_NEWGRF_PRINT_WORD_SIGNED:      *argv = _newgrf_textrefstack.PopSignedWord();    break;
    01029
    01030       case SCC_NEWGRF_PRINT_WORD_HEX:
    01031       case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
    01032       case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
    01033       case SCC_NEWGRF_PRINT_WORD_POWER:
    01034       case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
    01035       case SCC_NEWGRF_PRINT_WORD_UNSIGNED:    *argv = _newgrf_textrefstack.PopUnsignedWord();  break;
    01036
    01037       case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
    01038       case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
    01039       case SCC_NEWGRF_PRINT_DWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
    01040
    01041       case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
    01042       case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:  *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
    01043
    01044       case SCC_NEWGRF_DISCARD_WORD:           _newgrf_textrefstack.PopUnsignedWord(); break;
    01045
    01046       case SCC_NEWGRF_ROTATE_TOP_4_WORDS:     _newgrf_textrefstack.RotateTop4Words(); break;
    01047       case SCC_NEWGRF_PUSH_WORD:              _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
    01048       case SCC_NEWGRF_UNPRINT:                *buff = max(*buff - Utf8Consume(str), buf_start); break;
    01049
    01050       case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
    01051       case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
    01052       case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
    01053         argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
    01054         argv[1] = _newgrf_textrefstack.PopUnsignedWord();
    01055         break;
    01056
    01057       case SCC_NEWGRF_PRINT_WORD_STRING_ID:
    01058         *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
    01059         break;
    01060     }
    01061   } else {
    01062     /* Consume additional parameter characters */
    01063     switch (scc) {
    01064       default: break;
    01065
    01066       case SCC_NEWGRF_PUSH_WORD:
    01067       case SCC_NEWGRF_UNPRINT:
    01068         Utf8Consume(str);
    01069         break;
    01070     }
    01071   }
    01072
    01073   switch (scc) {
    01074     default: NOT_REACHED();
    01075     case SCC_NEWGRF_PRINT_DWORD_SIGNED:
    01076     case SCC_NEWGRF_PRINT_WORD_SIGNED:
    01077     case SCC_NEWGRF_PRINT_BYTE_SIGNED:
    01078     case SCC_NEWGRF_PRINT_WORD_UNSIGNED:
    01079       return SCC_COMMA;
    01080
    01081     case SCC_NEWGRF_PRINT_BYTE_HEX:
    01082     case SCC_NEWGRF_PRINT_WORD_HEX:
    01083     case SCC_NEWGRF_PRINT_DWORD_HEX:
    01084     case SCC_NEWGRF_PRINT_QWORD_HEX:
    01085       return SCC_HEX;
    01086
    01087     case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
    01088     case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
    01089       return SCC_CURRENCY_LONG;
    01090
    01091     case SCC_NEWGRF_PRINT_WORD_STRING_ID:
    01092       return SCC_NEWGRF_PRINT_WORD_STRING_ID;
    01093
    01094     case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
    01095     case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
    01096       return SCC_DATE_LONG;
    01097
    01098     case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:
    01099     case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
    01100       return SCC_DATE_SHORT;
    01101
    01102     case SCC_NEWGRF_PRINT_WORD_SPEED:
    01103       return SCC_VELOCITY;
    01104
    01105     case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
    01106       return SCC_VOLUME_LONG;
    01107
    01108     case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
    01109       return SCC_VOLUME_SHORT;
    01110
    01111     case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
    01112       return SCC_WEIGHT_LONG;
    01113
    01114     case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
    01115       return SCC_WEIGHT_SHORT;
    01116
    01117     case SCC_NEWGRF_PRINT_WORD_POWER:
    01118       return SCC_POWER;
    01119
    01120     case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
    01121       return SCC_CARGO_LONG;
    01122
    01123     case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
    01124       return SCC_CARGO_SHORT;
    01125
    01126     case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
    01127       return SCC_CARGO_TINY;
    01128
    01129     case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
    01130       return SCC_STATION_NAME;
    01131
    01132     case SCC_NEWGRF_DISCARD_WORD:
    01133     case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
    01134     case SCC_NEWGRF_PUSH_WORD:
    01135     case SCC_NEWGRF_UNPRINT:
    01136       return 0;
    01137   }
    01138 }