newgrf_text.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 "string_func.h"
00026 #include "date_type.h"
00027 #include "debug.h"
00028 #include "core/alloc_type.hpp"
00029 #include "core/smallmap_type.hpp"
00030 #include "language.h"
00031 
00032 #include "table/strings.h"
00033 #include "table/control_codes.h"
00034 
00035 #define GRFTAB  28
00036 #define TABSIZE 11
00037 
00045 StringID TTDPStringIDToOTTDStringIDMapping(StringID str)
00046 {
00047   /* StringID table for TextIDs 0x4E->0x6D */
00048   static const StringID units_volume[] = {
00049     STR_ITEMS,      STR_PASSENGERS, STR_TONS,       STR_BAGS,
00050     STR_LITERS,     STR_ITEMS,      STR_CRATES,     STR_TONS,
00051     STR_TONS,       STR_TONS,       STR_TONS,       STR_BAGS,
00052     STR_TONS,       STR_TONS,       STR_TONS,       STR_BAGS,
00053     STR_TONS,       STR_TONS,       STR_BAGS,       STR_LITERS,
00054     STR_TONS,       STR_LITERS,     STR_TONS,       STR_ITEMS,
00055     STR_BAGS,       STR_LITERS,     STR_TONS,       STR_ITEMS,
00056     STR_TONS,       STR_ITEMS,      STR_LITERS,     STR_ITEMS
00057   };
00058 
00059   /* A string straight from a NewGRF; no need to remap this as it's already mapped. */
00060   if (IsInsideMM(str, 0xD000, 0xD7FF)) return str;
00061 
00062 #define TEXTID_TO_STRINGID(begin, end, stringid, stringend) \
00063   assert_compile(stringend - stringid == end - begin); \
00064   if (str >= begin && str <= end) return str + (stringid - begin)
00065 
00066   /* We have some changes in our cargo strings, resulting in some missing. */
00067   TEXTID_TO_STRINGID(0x000E, 0x002D, STR_CARGO_PLURAL_NOTHING,                      STR_CARGO_PLURAL_FIZZY_DRINKS);
00068   TEXTID_TO_STRINGID(0x002E, 0x004D, STR_CARGO_SINGULAR_NOTHING,                    STR_CARGO_SINGULAR_FIZZY_DRINK);
00069   if (str >= 0x004E && str <= 0x006D) return units_volume[str - 0x004E];
00070   TEXTID_TO_STRINGID(0x006E, 0x008D, STR_QUANTITY_NOTHING,                          STR_QUANTITY_FIZZY_DRINKS);
00071   TEXTID_TO_STRINGID(0x008E, 0x00AD, STR_ABBREV_NOTHING,                            STR_ABBREV_FIZZY_DRINKS);
00072   TEXTID_TO_STRINGID(0x00D1, 0x00E0, STR_COLOUR_DARK_BLUE,                          STR_COLOUR_WHITE);
00073 
00074   /* Map building names according to our lang file changes. There are several
00075    * ranges of house ids, all of which need to be remapped to allow newgrfs
00076    * to use original house names. */
00077   TEXTID_TO_STRINGID(0x200F, 0x201F, STR_TOWN_BUILDING_NAME_TALL_OFFICE_BLOCK_1,    STR_TOWN_BUILDING_NAME_OLD_HOUSES_1);
00078   TEXTID_TO_STRINGID(0x2036, 0x2041, STR_TOWN_BUILDING_NAME_COTTAGES_1,             STR_TOWN_BUILDING_NAME_SHOPPING_MALL_1);
00079   TEXTID_TO_STRINGID(0x2059, 0x205C, STR_TOWN_BUILDING_NAME_IGLOO_1,                STR_TOWN_BUILDING_NAME_PIGGY_BANK_1);
00080 
00081   /* Same thing for industries */
00082   TEXTID_TO_STRINGID(0x4802, 0x4826, STR_INDUSTRY_NAME_COAL_MINE,                   STR_INDUSTRY_NAME_SUGAR_MINE);
00083   TEXTID_TO_STRINGID(0x482D, 0x482E, STR_NEWS_INDUSTRY_CONSTRUCTION,                STR_NEWS_INDUSTRY_PLANTED);
00084   TEXTID_TO_STRINGID(0x4832, 0x4834, STR_NEWS_INDUSTRY_CLOSURE_GENERAL,             STR_NEWS_INDUSTRY_CLOSURE_LACK_OF_TREES);
00085   TEXTID_TO_STRINGID(0x4835, 0x4838, STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_GENERAL, STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_FARM);
00086   TEXTID_TO_STRINGID(0x4839, 0x483A, STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_GENERAL, STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_FARM);
00087 
00088   switch (str) {
00089     case 0x4830: return STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY;
00090     case 0x4831: return STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED;
00091     case 0x483B: return STR_ERROR_CAN_ONLY_BE_POSITIONED;
00092   }
00093 #undef TEXTID_TO_STRINGID
00094 
00095   if (str == STR_NULL) return STR_EMPTY;
00096 
00097   DEBUG(grf, 0, "Unknown StringID 0x%04X remapped to STR_EMPTY. Please open a Feature Request if you need it", str);
00098 
00099   return STR_EMPTY;
00100 }
00101 
00107 enum GRFBaseLanguages {
00108   GRFLB_AMERICAN    = 0x01,
00109   GRFLB_ENGLISH     = 0x02,
00110   GRFLB_GERMAN      = 0x04,
00111   GRFLB_FRENCH      = 0x08,
00112   GRFLB_SPANISH     = 0x10,
00113   GRFLB_GENERIC     = 0x80,
00114 };
00115 
00116 enum GRFExtendedLanguages {
00117   GRFLX_AMERICAN    = 0x00,
00118   GRFLX_ENGLISH     = 0x01,
00119   GRFLX_GERMAN      = 0x02,
00120   GRFLX_FRENCH      = 0x03,
00121   GRFLX_SPANISH     = 0x04,
00122   GRFLX_UNSPECIFIED = 0x7F,
00123 };
00124 
00130 struct GRFText {
00131 public:
00142   static GRFText *New(byte langid, const char *text, size_t len)
00143   {
00144     return new (len) GRFText(langid, text, len);
00145   }
00146 
00152   static GRFText *Copy(GRFText *orig)
00153   {
00154     return GRFText::New(orig->langid, orig->text, orig->len);
00155   }
00156 
00162   void *operator new(size_t size)
00163   {
00164     NOT_REACHED();
00165   }
00166 
00171   void operator delete(void *p)
00172   {
00173     free(p);
00174   }
00175 private:
00182   GRFText(byte langid_, const char *text_, size_t len_) : next(NULL), len(len_), langid(langid_)
00183   {
00184     /* We need to use memcpy instead of strcpy due to
00185      * the possibility of "choice lists" and therefor
00186      * intermediate string terminators. */
00187     memcpy(this->text, text_, len);
00188   }
00189 
00196   void *operator new(size_t size, size_t extra)
00197   {
00198     return MallocT<byte>(size + extra);
00199   }
00200 
00201 public:
00202   GRFText *next; 
00203   size_t len;    
00204   byte langid;   
00205   char text[];   
00206 };
00207 
00208 
00214 struct GRFTextEntry {
00215   uint32 grfid;
00216   uint16 stringid;
00217   StringID def_string;
00218   GRFText *textholder;
00219 };
00220 
00221 
00222 static uint _num_grf_texts = 0;
00223 static GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
00224 static byte _currentLangID = GRFLX_ENGLISH;  
00225 
00232 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
00233 {
00234   const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
00235   for (const Mapping *m = map.Begin(); m != map.End(); m++) {
00236     if (m->newgrf_id == newgrf_id) return m->openttd_id;
00237   }
00238   return -1;
00239 }
00240 
00247 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
00248 {
00249   const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
00250   for (const Mapping *m = map.Begin(); m != map.End(); m++) {
00251     if (m->openttd_id == openttd_id) return m->newgrf_id;
00252   }
00253   return -1;
00254 }
00255 
00257 struct UnmappedChoiceList : ZeroedMemoryAllocator {
00259   ~UnmappedChoiceList()
00260   {
00261     for (SmallPair<byte, char *> *p = this->strings.Begin(); p < this->strings.End(); p++) {
00262       free(p->second);
00263     }
00264   }
00265 
00272   UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
00273     type(type), old_d(old_d), offset(offset)
00274   {
00275   }
00276 
00277   StringControlCode type; 
00278   char *old_d;            
00279   int offset;             
00280 
00282   SmallMap<byte, char *> strings;
00283 
00289   char *Flush(const LanguageMap *lm)
00290   {
00291     if (!this->strings.Contains(0)) {
00292       /* In case of a (broken) NewGRF without a default,
00293        * assume an empty string. */
00294       grfmsg(1, "choice list misses default value");
00295       this->strings[0] = strdup("");
00296     }
00297 
00298     char *d = old_d;
00299     if (lm == NULL && this->type != SCC_PLURAL_LIST) {
00300       /* In case there is no mapping, just ignore everything but the default.
00301        * A probable cause for this happening is when the language file has
00302        * been removed by the user and as such no mapping could be made. */
00303       size_t len = strlen(this->strings[0]);
00304       memcpy(d, this->strings[0], len);
00305       return d + len;
00306     }
00307 
00308     d += Utf8Encode(d, this->type);
00309 
00310     if (this->type == SCC_SWITCH_CASE) {
00311       /*
00312        * Format for case switch:
00313        * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
00314        * Each LEN is printed using 2 bytes in big endian order.
00315        */
00316 
00317       /* "<NUM CASES>" */
00318       int count = 0;
00319       for (uint8 i = 0; i < _current_language->num_cases; i++) {
00320         /* Count the ones we have a mapped string for. */
00321         if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
00322       }
00323       *d++ = count;
00324 
00325       for (uint8 i = 0; i < _current_language->num_cases; i++) {
00326         /* Resolve the string we're looking for. */
00327         int idx = lm->GetReverseMapping(i, false);
00328         if (!this->strings.Contains(idx)) continue;
00329         char *str = this->strings[idx];
00330 
00331         /* "<CASEn>" */
00332         *d++ = i + 1;
00333 
00334         /* "<LENn>" */
00335         size_t len = strlen(str) + 1;
00336         *d++ = GB(len, 8, 8);
00337         *d++ = GB(len, 0, 8);
00338 
00339         /* "<STRINGn>" */
00340         memcpy(d, str, len);
00341         d += len;
00342       }
00343 
00344       /* "<STRINGDEFAULT>" */
00345       size_t len = strlen(this->strings[0]) + 1;
00346       memcpy(d, this->strings[0], len);
00347       d += len;
00348     } else {
00349       if (this->type == SCC_PLURAL_LIST) {
00350         *d++ = lm->plural_form;
00351       }
00352 
00353       /*
00354        * Format for choice list:
00355        * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
00356        */
00357 
00358       /* "<OFFSET>" */
00359       *d++ = this->offset - 0x80;
00360 
00361       /* "<NUM CHOICES>" */
00362       int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
00363       *d++ = count;
00364 
00365       /* "<LENs>" */
00366       for (int i = 0; i < count; i++) {
00367         int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
00368         const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
00369         size_t len = strlen(str) + 1;
00370         if (len > 0xFF) grfmsg(1, "choice list string is too long");
00371         *d++ = GB(len, 0, 8);
00372       }
00373 
00374       /* "<STRINGs>" */
00375       for (int i = 0; i < count; i++) {
00376         int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
00377         const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
00378         /* Limit the length of the string we copy to 0xFE. The length is written above
00379          * as a byte and we need room for the final '\0'. */
00380         size_t len = min<size_t>(0xFE, strlen(str));
00381         memcpy(d, str, len);
00382         d += len;
00383         *d++ = '\0';
00384       }
00385     }
00386     return d;
00387   }
00388 };
00389 
00399 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen)
00400 {
00401   char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
00402   char *d = tmp;
00403   bool unicode = false;
00404   WChar c;
00405   size_t len = Utf8Decode(&c, str);
00406 
00407   /* Helper variable for a possible (string) mapping. */
00408   UnmappedChoiceList *mapping = NULL;
00409 
00410   if (c == NFO_UTF8_IDENTIFIER) {
00411     unicode = true;
00412     str += len;
00413   }
00414 
00415   for (;;) {
00416     if (unicode && Utf8EncodedCharLen(*str) != 0) {
00417       c = Utf8Consume(&str);
00418       /* 'Magic' range of control codes. */
00419       if (GB(c, 8, 8) == 0xE0) {
00420         c = GB(c, 0, 8);
00421       } else if (c >= 0x20) {
00422         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
00423         d += Utf8Encode(d, c);
00424         continue;
00425       }
00426     } else {
00427       c = (byte)*str++;
00428     }
00429     if (c == '\0') break;
00430 
00431     switch (c) {
00432       case 0x01:
00433         if (str[0] == '\0') goto string_end;
00434         d += Utf8Encode(d, SCC_SETX);
00435         *d++ = *str++;
00436         break;
00437       case 0x0A: break;
00438       case 0x0D:
00439         if (allow_newlines) {
00440           *d++ = 0x0A;
00441         } else {
00442           grfmsg(1, "Detected newline in string that does not allow one");
00443         }
00444         break;
00445       case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
00446       case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
00447       case 0x1F:
00448         if (str[0] == '\0' || str[1] == '\0') goto string_end;
00449         d += Utf8Encode(d, SCC_SETXY);
00450         *d++ = *str++;
00451         *d++ = *str++;
00452         break;
00453       case 0x7B:
00454       case 0x7C:
00455       case 0x7D:
00456       case 0x7E:
00457       case 0x7F:
00458       case 0x80: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
00459       case 0x81: {
00460         if (str[0] == '\0' || str[1] == '\0') goto string_end;
00461         StringID string;
00462         string  = ((uint8)*str++);
00463         string |= ((uint8)*str++) << 8;
00464         d += Utf8Encode(d, SCC_NEWGRF_STRINL);
00465         d += Utf8Encode(d, MapGRFStringID(grfid, string));
00466         break;
00467       }
00468       case 0x82:
00469       case 0x83:
00470       case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
00471       case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD);       break;
00472       case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
00473       case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG);  break;
00474       case 0x88: d += Utf8Encode(d, SCC_BLUE);    break;
00475       case 0x89: d += Utf8Encode(d, SCC_SILVER);  break;
00476       case 0x8A: d += Utf8Encode(d, SCC_GOLD);    break;
00477       case 0x8B: d += Utf8Encode(d, SCC_RED);     break;
00478       case 0x8C: d += Utf8Encode(d, SCC_PURPLE);  break;
00479       case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
00480       case 0x8E: d += Utf8Encode(d, SCC_ORANGE);  break;
00481       case 0x8F: d += Utf8Encode(d, SCC_GREEN);   break;
00482       case 0x90: d += Utf8Encode(d, SCC_YELLOW);  break;
00483       case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
00484       case 0x92: d += Utf8Encode(d, SCC_CREAM);   break;
00485       case 0x93: d += Utf8Encode(d, SCC_BROWN);   break;
00486       case 0x94: d += Utf8Encode(d, SCC_WHITE);   break;
00487       case 0x95: d += Utf8Encode(d, SCC_LTBLUE);  break;
00488       case 0x96: d += Utf8Encode(d, SCC_GRAY);    break;
00489       case 0x97: d += Utf8Encode(d, SCC_DKBLUE);  break;
00490       case 0x98: d += Utf8Encode(d, SCC_BLACK);   break;
00491       case 0x9A: {
00492         int code = *str++;
00493         switch (code) {
00494           case 0x00: goto string_end;
00495           case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
00496           /* 0x02: ignore next colour byte is not supported. It works on the final
00497            * string and as such hooks into the string drawing routine. At that
00498            * point many things already happened, such as splitting up of strings
00499            * when drawn over multiple lines or right-to-left translations, which
00500            * make the behaviour peculiar, e.g. only happening at specific width
00501            * of windows. Or we need to add another pass over the string to just
00502            * support this. As such it is not implemented in OpenTTD. */
00503           case 0x03: {
00504             if (str[0] == '\0' || str[1] == '\0') goto string_end;
00505             uint16 tmp  = ((uint8)*str++);
00506             tmp        |= ((uint8)*str++) << 8;
00507             d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD);
00508             d += Utf8Encode(d, tmp);
00509             break;
00510           }
00511           case 0x04:
00512             if (str[0] == '\0') goto string_end;
00513             d += Utf8Encode(d, SCC_NEWGRF_UNPRINT);
00514             d += Utf8Encode(d, *str++);
00515             break;
00516           case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX);          break;
00517           case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX);          break;
00518           case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX);         break;
00519           /* 0x09, 0x0A are TTDPatch internal use only string codes. */
00520           case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX);         break;
00521           case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
00522           case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG);  break;
00523           case 0x0E:
00524           case 0x0F: {
00525             if (str[0] == '\0') goto string_end;
00526             const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
00527             int index = *str++;
00528             int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1;
00529             if (mapped >= 0) {
00530               d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
00531               d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
00532             }
00533             break;
00534           }
00535 
00536           case 0x10:
00537           case 0x11:
00538             if (str[0] == '\0') goto string_end;
00539             if (mapping == NULL) {
00540               if (code == 0x10) str++; // Skip the index
00541               grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
00542               break;
00543             } else {
00544               /* Terminate the previous string. */
00545               *d = '\0';
00546               int index = (code == 0x10 ? *str++ : 0);
00547               if (mapping->strings.Contains(index)) {
00548                 grfmsg(1, "duplicate choice list string, ignoring");
00549                 d++;
00550               } else {
00551                 d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
00552               }
00553             }
00554             break;
00555 
00556           case 0x12:
00557             if (mapping == NULL) {
00558               grfmsg(1, "choice list end marker found when not expected");
00559             } else {
00560               /* Terminate the previous string. */
00561               *d = '\0';
00562 
00563               /* Now we can start flushing everything and clean everything up. */
00564               d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
00565               delete mapping;
00566               mapping = NULL;
00567             }
00568             break;
00569 
00570           case 0x13:
00571           case 0x14:
00572           case 0x15:
00573             if (str[0] == '\0') goto string_end;
00574             if (mapping != NULL) {
00575               grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
00576               if (code != 0x14) str++;
00577             } else {
00578               static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
00579               mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
00580             }
00581             break;
00582 
00583           case 0x16:
00584           case 0x17:
00585           case 0x18:
00586           case 0x19:
00587           case 0x1A: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16); break;
00588 
00589           default:
00590             grfmsg(1, "missing handler for extended format code");
00591             break;
00592         }
00593         break;
00594       }
00595 
00596       case 0x9E: d += Utf8Encode(d, 0x20AC);               break; // Euro
00597       case 0x9F: d += Utf8Encode(d, 0x0178);               break; // Y with diaeresis
00598       case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW);         break;
00599       case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW);       break;
00600       case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK);        break;
00601       case 0xAD: d += Utf8Encode(d, SCC_CROSS);            break;
00602       case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW);      break;
00603       case 0xB4: d += Utf8Encode(d, SCC_TRAIN);            break;
00604       case 0xB5: d += Utf8Encode(d, SCC_LORRY);            break;
00605       case 0xB6: d += Utf8Encode(d, SCC_BUS);              break;
00606       case 0xB7: d += Utf8Encode(d, SCC_PLANE);            break;
00607       case 0xB8: d += Utf8Encode(d, SCC_SHIP);             break;
00608       case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1);   break;
00609       case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW);   break;
00610       case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
00611       default:
00612         /* Validate any unhandled character */
00613         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
00614         d += Utf8Encode(d, c);
00615         break;
00616     }
00617   }
00618 
00619 string_end:
00620   if (mapping != NULL) {
00621     grfmsg(1, "choice list was incomplete, the whole list is ignored");
00622     delete mapping;
00623   }
00624 
00625   *d = '\0';
00626   if (olen != NULL) *olen = d - tmp + 1;
00627   tmp = ReallocT(tmp, d - tmp + 1);
00628   return tmp;
00629 }
00630 
00636 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
00637 {
00638   GRFText **ptext, *text;
00639 
00640   /* Loop through all languages and see if we can replace a string */
00641   for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
00642     if (text->langid == text_to_add->langid) {
00643       text_to_add->next = text->next;
00644       *ptext = text_to_add;
00645       delete text;
00646       return;
00647     }
00648   }
00649 
00650   /* If a string wasn't replaced, then we must append the new string */
00651   *ptext = text_to_add;
00652 }
00653 
00663 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
00664 {
00665   int len;
00666   char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
00667   GRFText *newtext = GRFText::New(langid, translatedtext, len);
00668   free(translatedtext);
00669 
00670   AddGRFTextToList(list, newtext);
00671 }
00672 
00679 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
00680 {
00681   AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
00682 }
00683 
00689 GRFText *DuplicateGRFText(GRFText *orig)
00690 {
00691   GRFText *newtext = NULL;
00692   GRFText **ptext = &newtext;
00693   for (; orig != NULL; orig = orig->next) {
00694     *ptext = GRFText::Copy(orig);
00695     ptext = &(*ptext)->next;
00696   }
00697   return newtext;
00698 }
00699 
00703 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
00704 {
00705   char *translatedtext;
00706   uint id;
00707 
00708   /* When working with the old language scheme (grf_version is less than 7) and
00709    * English or American is among the set bits, simply add it as English in
00710    * the new scheme, i.e. as langid = 1.
00711    * If English is set, it is pretty safe to assume the translations are not
00712    * actually translated.
00713    */
00714   if (!new_scheme) {
00715     if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
00716       langid_to_add = GRFLX_ENGLISH;
00717     } else {
00718       StringID ret = STR_EMPTY;
00719       if (langid_to_add & GRFLB_GERMAN)  ret = AddGRFString(grfid, stringid, GRFLX_GERMAN,  true, allow_newlines, text_to_add, def_string);
00720       if (langid_to_add & GRFLB_FRENCH)  ret = AddGRFString(grfid, stringid, GRFLX_FRENCH,  true, allow_newlines, text_to_add, def_string);
00721       if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
00722       return ret;
00723     }
00724   }
00725 
00726   for (id = 0; id < _num_grf_texts; id++) {
00727     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
00728       break;
00729     }
00730   }
00731 
00732   /* Too many strings allocated, return empty */
00733   if (id == lengthof(_grf_text)) return STR_EMPTY;
00734 
00735   int len;
00736   translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
00737 
00738   GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
00739 
00740   free(translatedtext);
00741 
00742   /* If we didn't find our stringid and grfid in the list, allocate a new id */
00743   if (id == _num_grf_texts) _num_grf_texts++;
00744 
00745   if (_grf_text[id].textholder == NULL) {
00746     _grf_text[id].grfid      = grfid;
00747     _grf_text[id].stringid   = stringid;
00748     _grf_text[id].def_string = def_string;
00749   }
00750   AddGRFTextToList(&_grf_text[id].textholder, newtext);
00751 
00752   grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
00753 
00754   return (GRFTAB << TABSIZE) + id;
00755 }
00756 
00757 /* Used to remember the grfid that the last retrieved string came from */
00758 static uint32 _last_grfid = 0;
00759 
00763 StringID GetGRFStringID(uint32 grfid, uint16 stringid)
00764 {
00765   uint id;
00766 
00767   /* grfid is zero when we're being called via an include */
00768   if (grfid == 0) grfid = _last_grfid;
00769 
00770   for (id = 0; id < _num_grf_texts; id++) {
00771     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
00772       return (GRFTAB << TABSIZE) + id;
00773     }
00774   }
00775 
00776   return STR_UNDEFINED;
00777 }
00778 
00779 
00787 const char *GetGRFStringFromGRFText(const GRFText *text)
00788 {
00789   const char *default_text = NULL;
00790 
00791   /* Search the list of lang-strings of this stringid for current lang */
00792   for (; text != NULL; text = text->next) {
00793     if (text->langid == _currentLangID) return text->text;
00794 
00795     /* If the current string is English or American, set it as the
00796      * fallback language if the specific language isn't available. */
00797     if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
00798       default_text = text->text;
00799     }
00800   }
00801 
00802   return default_text;
00803 }
00804 
00808 const char *GetGRFStringPtr(uint16 stringid)
00809 {
00810   assert(_grf_text[stringid].grfid != 0);
00811 
00812   /* Remember this grfid in case the string has included text */
00813   _last_grfid = _grf_text[stringid].grfid;
00814 
00815   const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
00816   if (str != NULL) return str;
00817 
00818   /* Use the default string ID if the fallback string isn't available */
00819   return GetStringPtr(_grf_text[stringid].def_string);
00820 }
00821 
00830 void SetCurrentGrfLangID(byte language_id)
00831 {
00832   _currentLangID = language_id;
00833 }
00834 
00835 bool CheckGrfLangID(byte lang_id, byte grf_version)
00836 {
00837   if (grf_version < 7) {
00838     switch (_currentLangID) {
00839       case GRFLX_GERMAN:  return (lang_id & GRFLB_GERMAN)  != 0;
00840       case GRFLX_FRENCH:  return (lang_id & GRFLB_FRENCH)  != 0;
00841       case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
00842       default:            return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
00843     }
00844   }
00845 
00846   return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
00847 }
00848 
00853 void CleanUpGRFText(GRFText *grftext)
00854 {
00855   while (grftext != NULL) {
00856     GRFText *grftext2 = grftext->next;
00857     delete grftext;
00858     grftext = grftext2;
00859   }
00860 }
00861 
00866 void CleanUpStrings()
00867 {
00868   uint id;
00869 
00870   for (id = 0; id < _num_grf_texts; id++) {
00871     CleanUpGRFText(_grf_text[id].textholder);
00872     _grf_text[id].grfid      = 0;
00873     _grf_text[id].stringid   = 0;
00874     _grf_text[id].textholder = NULL;
00875   }
00876 
00877   _num_grf_texts = 0;
00878 }
00879 
00880 struct TextRefStack {
00881   byte stack[0x30];
00882   byte position;
00883   bool used;
00884 
00885   TextRefStack() : used(false) {}
00886 
00887   TextRefStack(const TextRefStack &stack) :
00888     position(stack.position),
00889     used(stack.used)
00890   {
00891     memcpy(this->stack, stack.stack, sizeof(this->stack));
00892   }
00893 
00894   uint8  PopUnsignedByte()  { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
00895   int8   PopSignedByte()    { return (int8)this->PopUnsignedByte(); }
00896 
00897   uint16 PopUnsignedWord()
00898   {
00899     uint16 val = this->PopUnsignedByte();
00900     return val | (this->PopUnsignedByte() << 8);
00901   }
00902   int16  PopSignedWord()    { return (int32)this->PopUnsignedWord(); }
00903 
00904   uint32 PopUnsignedDWord()
00905   {
00906     uint32 val = this->PopUnsignedWord();
00907     return val | (this->PopUnsignedWord() << 16);
00908   }
00909   int32  PopSignedDWord()   { return (int32)this->PopUnsignedDWord(); }
00910 
00911   uint64 PopUnsignedQWord()
00912   {
00913     uint64 val = this->PopUnsignedDWord();
00914     return val | (((uint64)this->PopUnsignedDWord()) << 32);
00915   }
00916   int64  PopSignedQWord()   { return (int64)this->PopUnsignedQWord(); }
00917 
00919   void RotateTop4Words()
00920   {
00921     byte tmp[2];
00922     for (int i = 0; i  < 2; i++) tmp[i] = this->stack[this->position + i + 6];
00923     for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
00924     for (int i = 0; i  < 2; i++) this->stack[this->position + i] = tmp[i];
00925   }
00926 
00927   void PushWord(uint16 word)
00928   {
00929     if (this->position >= 2) {
00930       this->position -= 2;
00931     } else {
00932       for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
00933         this->stack[i] = this->stack[i - 2];
00934       }
00935     }
00936     this->stack[this->position]     = GB(word, 0, 8);
00937     this->stack[this->position + 1] = GB(word, 8, 8);
00938   }
00939 
00940   void ResetStack()  { this->position = 0; this->used = true; }
00941   void RewindStack() { this->position = 0; }
00942 };
00943 
00945 static TextRefStack _newgrf_textrefstack;
00946 
00951 bool UsingNewGRFTextStack()
00952 {
00953   return _newgrf_textrefstack.used;
00954 }
00955 
00960 struct TextRefStack *CreateTextRefStackBackup()
00961 {
00962   return new TextRefStack(_newgrf_textrefstack);
00963 }
00964 
00969 void RestoreTextRefStackBackup(struct TextRefStack *backup)
00970 {
00971   _newgrf_textrefstack = *backup;
00972   delete backup;
00973 }
00974 
00992 void StartTextRefStackUsage(byte numEntries, const uint32 *values)
00993 {
00994   extern TemporaryStorageArray<int32, 0x110> _temp_store;
00995 
00996   _newgrf_textrefstack.ResetStack();
00997 
00998   byte *p = _newgrf_textrefstack.stack;
00999   for (uint i = 0; i < numEntries; i++) {
01000     uint32 value = values != NULL ? values[i] : _temp_store.GetValue(0x100 + i);
01001     for (uint j = 0; j < 32; j += 8) {
01002       *p = GB(value, j, 8);
01003       p++;
01004     }
01005   }
01006 }
01007 
01009 void StopTextRefStackUsage()
01010 {
01011   _newgrf_textrefstack.used = false;
01012 }
01013 
01014 void RewindTextRefStack()
01015 {
01016   _newgrf_textrefstack.RewindStack();
01017 }
01018 
01028 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, bool modify_argv)
01029 {
01030   if (_newgrf_textrefstack.used && modify_argv) {
01031     switch (scc) {
01032       default: NOT_REACHED();
01033       case SCC_NEWGRF_PRINT_BYTE_SIGNED:      *argv = _newgrf_textrefstack.PopSignedByte();    break;
01034       case SCC_NEWGRF_PRINT_QWORD_CURRENCY:   *argv = _newgrf_textrefstack.PopSignedQWord();   break;
01035 
01036       case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
01037       case SCC_NEWGRF_PRINT_DWORD_SIGNED:     *argv = _newgrf_textrefstack.PopSignedDWord();   break;
01038 
01039       case SCC_NEWGRF_PRINT_BYTE_HEX:         *argv = _newgrf_textrefstack.PopUnsignedByte();  break;
01040       case SCC_NEWGRF_PRINT_QWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
01041 
01042       case SCC_NEWGRF_PRINT_WORD_SPEED:
01043       case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
01044       case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
01045       case SCC_NEWGRF_PRINT_WORD_SIGNED:      *argv = _newgrf_textrefstack.PopSignedWord();    break;
01046 
01047       case SCC_NEWGRF_PRINT_WORD_HEX:
01048       case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
01049       case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
01050       case SCC_NEWGRF_PRINT_WORD_POWER:
01051       case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
01052       case SCC_NEWGRF_PRINT_WORD_UNSIGNED:    *argv = _newgrf_textrefstack.PopUnsignedWord();  break;
01053 
01054       case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
01055       case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
01056       case SCC_NEWGRF_PRINT_DWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
01057 
01058       case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
01059       case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:  *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
01060 
01061       case SCC_NEWGRF_DISCARD_WORD:           _newgrf_textrefstack.PopUnsignedWord(); break;
01062 
01063       case SCC_NEWGRF_ROTATE_TOP_4_WORDS:     _newgrf_textrefstack.RotateTop4Words(); break;
01064       case SCC_NEWGRF_PUSH_WORD:              _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
01065       case SCC_NEWGRF_UNPRINT:                *buff = max(*buff - Utf8Consume(str), buf_start); break;
01066 
01067       case SCC_NEWGRF_PRINT_WORD_STRING_ID:
01068         *argv = TTDPStringIDToOTTDStringIDMapping(_newgrf_textrefstack.PopUnsignedWord());
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_STATION_NAME:
01121       return SCC_STATION_NAME;
01122 
01123     case SCC_NEWGRF_DISCARD_WORD:
01124     case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
01125     case SCC_NEWGRF_PUSH_WORD:
01126     case SCC_NEWGRF_UNPRINT:
01127       return 0;
01128   }
01129 }