newgrf_text.cpp

Go to the documentation of this file.
00001 /* $Id: newgrf_text.cpp 26244 2014-01-12 18:01:33Z 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   }
01062 
01063   switch (scc) {
01064     default: NOT_REACHED();
01065     case SCC_NEWGRF_PRINT_DWORD_SIGNED:
01066     case SCC_NEWGRF_PRINT_WORD_SIGNED:
01067     case SCC_NEWGRF_PRINT_BYTE_SIGNED:
01068     case SCC_NEWGRF_PRINT_WORD_UNSIGNED:
01069       return SCC_COMMA;
01070 
01071     case SCC_NEWGRF_PRINT_BYTE_HEX:
01072     case SCC_NEWGRF_PRINT_WORD_HEX:
01073     case SCC_NEWGRF_PRINT_DWORD_HEX:
01074     case SCC_NEWGRF_PRINT_QWORD_HEX:
01075       return SCC_HEX;
01076 
01077     case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
01078     case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
01079       return SCC_CURRENCY_LONG;
01080 
01081     case SCC_NEWGRF_PRINT_WORD_STRING_ID:
01082       return SCC_NEWGRF_PRINT_WORD_STRING_ID;
01083 
01084     case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
01085     case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
01086       return SCC_DATE_LONG;
01087 
01088     case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:
01089     case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
01090       return SCC_DATE_SHORT;
01091 
01092     case SCC_NEWGRF_PRINT_WORD_SPEED:
01093       return SCC_VELOCITY;
01094 
01095     case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
01096       return SCC_VOLUME_LONG;
01097 
01098     case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
01099       return SCC_VOLUME_SHORT;
01100 
01101     case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
01102       return SCC_WEIGHT_LONG;
01103 
01104     case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
01105       return SCC_WEIGHT_SHORT;
01106 
01107     case SCC_NEWGRF_PRINT_WORD_POWER:
01108       return SCC_POWER;
01109 
01110     case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
01111       return SCC_CARGO_LONG;
01112 
01113     case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
01114       return SCC_CARGO_SHORT;
01115 
01116     case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
01117       return SCC_CARGO_TINY;
01118 
01119     case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
01120       return SCC_STATION_NAME;
01121 
01122     case SCC_NEWGRF_DISCARD_WORD:
01123     case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
01124     case SCC_NEWGRF_PUSH_WORD:
01125     case SCC_NEWGRF_UNPRINT:
01126       return 0;
01127   }
01128 }