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 
00400 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
00401 {
00402   char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
00403   char *d = tmp;
00404   bool unicode = false;
00405   WChar c;
00406   size_t len = Utf8Decode(&c, str);
00407 
00408   /* Helper variable for a possible (string) mapping. */
00409   UnmappedChoiceList *mapping = NULL;
00410 
00411   if (c == NFO_UTF8_IDENTIFIER) {
00412     unicode = true;
00413     str += len;
00414   }
00415 
00416   for (;;) {
00417     if (unicode && Utf8EncodedCharLen(*str) != 0) {
00418       c = Utf8Consume(&str);
00419       /* 'Magic' range of control codes. */
00420       if (GB(c, 8, 8) == 0xE0) {
00421         c = GB(c, 0, 8);
00422       } else if (c >= 0x20) {
00423         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
00424         d += Utf8Encode(d, c);
00425         continue;
00426       }
00427     } else {
00428       c = (byte)*str++;
00429     }
00430     if (c == '\0') break;
00431 
00432     switch (c) {
00433       case 0x01:
00434         if (str[0] == '\0') goto string_end;
00435         d += Utf8Encode(d, SCC_SETX);
00436         *d++ = *str++;
00437         break;
00438       case 0x0A: break;
00439       case 0x0D:
00440         if (allow_newlines) {
00441           *d++ = 0x0A;
00442         } else {
00443           grfmsg(1, "Detected newline in string that does not allow one");
00444         }
00445         break;
00446       case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
00447       case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
00448       case 0x1F:
00449         if (str[0] == '\0' || str[1] == '\0') goto string_end;
00450         d += Utf8Encode(d, SCC_SETXY);
00451         *d++ = *str++;
00452         *d++ = *str++;
00453         break;
00454       case 0x7B:
00455       case 0x7C:
00456       case 0x7D:
00457       case 0x7E:
00458       case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
00459       case 0x80: d += Utf8Encode(d, byte80); break;
00460       case 0x81: {
00461         if (str[0] == '\0' || str[1] == '\0') goto string_end;
00462         StringID string;
00463         string  = ((uint8)*str++);
00464         string |= ((uint8)*str++) << 8;
00465         d += Utf8Encode(d, SCC_NEWGRF_STRINL);
00466         d += Utf8Encode(d, MapGRFStringID(grfid, string));
00467         break;
00468       }
00469       case 0x82:
00470       case 0x83:
00471       case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
00472       case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD);       break;
00473       case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
00474       case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG);  break;
00475       case 0x88: d += Utf8Encode(d, SCC_BLUE);    break;
00476       case 0x89: d += Utf8Encode(d, SCC_SILVER);  break;
00477       case 0x8A: d += Utf8Encode(d, SCC_GOLD);    break;
00478       case 0x8B: d += Utf8Encode(d, SCC_RED);     break;
00479       case 0x8C: d += Utf8Encode(d, SCC_PURPLE);  break;
00480       case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
00481       case 0x8E: d += Utf8Encode(d, SCC_ORANGE);  break;
00482       case 0x8F: d += Utf8Encode(d, SCC_GREEN);   break;
00483       case 0x90: d += Utf8Encode(d, SCC_YELLOW);  break;
00484       case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
00485       case 0x92: d += Utf8Encode(d, SCC_CREAM);   break;
00486       case 0x93: d += Utf8Encode(d, SCC_BROWN);   break;
00487       case 0x94: d += Utf8Encode(d, SCC_WHITE);   break;
00488       case 0x95: d += Utf8Encode(d, SCC_LTBLUE);  break;
00489       case 0x96: d += Utf8Encode(d, SCC_GRAY);    break;
00490       case 0x97: d += Utf8Encode(d, SCC_DKBLUE);  break;
00491       case 0x98: d += Utf8Encode(d, SCC_BLACK);   break;
00492       case 0x9A: {
00493         int code = *str++;
00494         switch (code) {
00495           case 0x00: goto string_end;
00496           case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
00497           /* 0x02: ignore next colour byte is not supported. It works on the final
00498            * string and as such hooks into the string drawing routine. At that
00499            * point many things already happened, such as splitting up of strings
00500            * when drawn over multiple lines or right-to-left translations, which
00501            * make the behaviour peculiar, e.g. only happening at specific width
00502            * of windows. Or we need to add another pass over the string to just
00503            * support this. As such it is not implemented in OpenTTD. */
00504           case 0x03: {
00505             if (str[0] == '\0' || str[1] == '\0') goto string_end;
00506             uint16 tmp  = ((uint8)*str++);
00507             tmp        |= ((uint8)*str++) << 8;
00508             d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD);
00509             d += Utf8Encode(d, tmp);
00510             break;
00511           }
00512           case 0x04:
00513             if (str[0] == '\0') goto string_end;
00514             d += Utf8Encode(d, SCC_NEWGRF_UNPRINT);
00515             d += Utf8Encode(d, *str++);
00516             break;
00517           case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX);          break;
00518           case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX);          break;
00519           case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX);         break;
00520           /* 0x09, 0x0A are TTDPatch internal use only string codes. */
00521           case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX);         break;
00522           case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
00523           case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG);  break;
00524           case 0x0E:
00525           case 0x0F: {
00526             if (str[0] == '\0') goto string_end;
00527             const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
00528             int index = *str++;
00529             int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1;
00530             if (mapped >= 0) {
00531               d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
00532               d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
00533             }
00534             break;
00535           }
00536 
00537           case 0x10:
00538           case 0x11:
00539             if (str[0] == '\0') goto string_end;
00540             if (mapping == NULL) {
00541               if (code == 0x10) str++; // Skip the index
00542               grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
00543               break;
00544             } else {
00545               /* Terminate the previous string. */
00546               *d = '\0';
00547               int index = (code == 0x10 ? *str++ : 0);
00548               if (mapping->strings.Contains(index)) {
00549                 grfmsg(1, "duplicate choice list string, ignoring");
00550                 d++;
00551               } else {
00552                 d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
00553               }
00554             }
00555             break;
00556 
00557           case 0x12:
00558             if (mapping == NULL) {
00559               grfmsg(1, "choice list end marker found when not expected");
00560             } else {
00561               /* Terminate the previous string. */
00562               *d = '\0';
00563 
00564               /* Now we can start flushing everything and clean everything up. */
00565               d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
00566               delete mapping;
00567               mapping = NULL;
00568             }
00569             break;
00570 
00571           case 0x13:
00572           case 0x14:
00573           case 0x15:
00574             if (str[0] == '\0') goto string_end;
00575             if (mapping != NULL) {
00576               grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
00577               if (code != 0x14) str++;
00578             } else {
00579               static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
00580               mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
00581             }
00582             break;
00583 
00584           case 0x16:
00585           case 0x17:
00586           case 0x18:
00587           case 0x19:
00588           case 0x1A: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16); break;
00589 
00590           default:
00591             grfmsg(1, "missing handler for extended format code");
00592             break;
00593         }
00594         break;
00595       }
00596 
00597       case 0x9E: d += Utf8Encode(d, 0x20AC);               break; // Euro
00598       case 0x9F: d += Utf8Encode(d, 0x0178);               break; // Y with diaeresis
00599       case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW);         break;
00600       case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW);       break;
00601       case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK);        break;
00602       case 0xAD: d += Utf8Encode(d, SCC_CROSS);            break;
00603       case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW);      break;
00604       case 0xB4: d += Utf8Encode(d, SCC_TRAIN);            break;
00605       case 0xB5: d += Utf8Encode(d, SCC_LORRY);            break;
00606       case 0xB6: d += Utf8Encode(d, SCC_BUS);              break;
00607       case 0xB7: d += Utf8Encode(d, SCC_PLANE);            break;
00608       case 0xB8: d += Utf8Encode(d, SCC_SHIP);             break;
00609       case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1);   break;
00610       case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW);   break;
00611       case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
00612       default:
00613         /* Validate any unhandled character */
00614         if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
00615         d += Utf8Encode(d, c);
00616         break;
00617     }
00618   }
00619 
00620 string_end:
00621   if (mapping != NULL) {
00622     grfmsg(1, "choice list was incomplete, the whole list is ignored");
00623     delete mapping;
00624   }
00625 
00626   *d = '\0';
00627   if (olen != NULL) *olen = d - tmp + 1;
00628   tmp = ReallocT(tmp, d - tmp + 1);
00629   return tmp;
00630 }
00631 
00637 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
00638 {
00639   GRFText **ptext, *text;
00640 
00641   /* Loop through all languages and see if we can replace a string */
00642   for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
00643     if (text->langid == text_to_add->langid) {
00644       text_to_add->next = text->next;
00645       *ptext = text_to_add;
00646       delete text;
00647       return;
00648     }
00649   }
00650 
00651   /* If a string wasn't replaced, then we must append the new string */
00652   *ptext = text_to_add;
00653 }
00654 
00664 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
00665 {
00666   int len;
00667   char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
00668   GRFText *newtext = GRFText::New(langid, translatedtext, len);
00669   free(translatedtext);
00670 
00671   AddGRFTextToList(list, newtext);
00672 }
00673 
00680 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
00681 {
00682   AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
00683 }
00684 
00690 GRFText *DuplicateGRFText(GRFText *orig)
00691 {
00692   GRFText *newtext = NULL;
00693   GRFText **ptext = &newtext;
00694   for (; orig != NULL; orig = orig->next) {
00695     *ptext = GRFText::Copy(orig);
00696     ptext = &(*ptext)->next;
00697   }
00698   return newtext;
00699 }
00700 
00704 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
00705 {
00706   char *translatedtext;
00707   uint id;
00708 
00709   /* When working with the old language scheme (grf_version is less than 7) and
00710    * English or American is among the set bits, simply add it as English in
00711    * the new scheme, i.e. as langid = 1.
00712    * If English is set, it is pretty safe to assume the translations are not
00713    * actually translated.
00714    */
00715   if (!new_scheme) {
00716     if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
00717       langid_to_add = GRFLX_ENGLISH;
00718     } else {
00719       StringID ret = STR_EMPTY;
00720       if (langid_to_add & GRFLB_GERMAN)  ret = AddGRFString(grfid, stringid, GRFLX_GERMAN,  true, allow_newlines, text_to_add, def_string);
00721       if (langid_to_add & GRFLB_FRENCH)  ret = AddGRFString(grfid, stringid, GRFLX_FRENCH,  true, allow_newlines, text_to_add, def_string);
00722       if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
00723       return ret;
00724     }
00725   }
00726 
00727   for (id = 0; id < _num_grf_texts; id++) {
00728     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
00729       break;
00730     }
00731   }
00732 
00733   /* Too many strings allocated, return empty */
00734   if (id == lengthof(_grf_text)) return STR_EMPTY;
00735 
00736   int len;
00737   translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
00738 
00739   GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
00740 
00741   free(translatedtext);
00742 
00743   /* If we didn't find our stringid and grfid in the list, allocate a new id */
00744   if (id == _num_grf_texts) _num_grf_texts++;
00745 
00746   if (_grf_text[id].textholder == NULL) {
00747     _grf_text[id].grfid      = grfid;
00748     _grf_text[id].stringid   = stringid;
00749     _grf_text[id].def_string = def_string;
00750   }
00751   AddGRFTextToList(&_grf_text[id].textholder, newtext);
00752 
00753   grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
00754 
00755   return (GRFTAB << TABSIZE) + id;
00756 }
00757 
00758 /* Used to remember the grfid that the last retrieved string came from */
00759 static uint32 _last_grfid = 0;
00760 
00764 StringID GetGRFStringID(uint32 grfid, uint16 stringid)
00765 {
00766   uint id;
00767 
00768   /* grfid is zero when we're being called via an include */
00769   if (grfid == 0) grfid = _last_grfid;
00770 
00771   for (id = 0; id < _num_grf_texts; id++) {
00772     if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
00773       return (GRFTAB << TABSIZE) + id;
00774     }
00775   }
00776 
00777   return STR_UNDEFINED;
00778 }
00779 
00780 
00788 const char *GetGRFStringFromGRFText(const GRFText *text)
00789 {
00790   const char *default_text = NULL;
00791 
00792   /* Search the list of lang-strings of this stringid for current lang */
00793   for (; text != NULL; text = text->next) {
00794     if (text->langid == _currentLangID) return text->text;
00795 
00796     /* If the current string is English or American, set it as the
00797      * fallback language if the specific language isn't available. */
00798     if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
00799       default_text = text->text;
00800     }
00801   }
00802 
00803   return default_text;
00804 }
00805 
00809 const char *GetGRFStringPtr(uint16 stringid)
00810 {
00811   assert(_grf_text[stringid].grfid != 0);
00812 
00813   /* Remember this grfid in case the string has included text */
00814   _last_grfid = _grf_text[stringid].grfid;
00815 
00816   const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
00817   if (str != NULL) return str;
00818 
00819   /* Use the default string ID if the fallback string isn't available */
00820   return GetStringPtr(_grf_text[stringid].def_string);
00821 }
00822 
00831 void SetCurrentGrfLangID(byte language_id)
00832 {
00833   _currentLangID = language_id;
00834 }
00835 
00836 bool CheckGrfLangID(byte lang_id, byte grf_version)
00837 {
00838   if (grf_version < 7) {
00839     switch (_currentLangID) {
00840       case GRFLX_GERMAN:  return (lang_id & GRFLB_GERMAN)  != 0;
00841       case GRFLX_FRENCH:  return (lang_id & GRFLB_FRENCH)  != 0;
00842       case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
00843       default:            return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
00844     }
00845   }
00846 
00847   return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
00848 }
00849 
00854 void CleanUpGRFText(GRFText *grftext)
00855 {
00856   while (grftext != NULL) {
00857     GRFText *grftext2 = grftext->next;
00858     delete grftext;
00859     grftext = grftext2;
00860   }
00861 }
00862 
00867 void CleanUpStrings()
00868 {
00869   uint id;
00870 
00871   for (id = 0; id < _num_grf_texts; id++) {
00872     CleanUpGRFText(_grf_text[id].textholder);
00873     _grf_text[id].grfid      = 0;
00874     _grf_text[id].stringid   = 0;
00875     _grf_text[id].textholder = NULL;
00876   }
00877 
00878   _num_grf_texts = 0;
00879 }
00880 
00881 struct TextRefStack {
00882   byte stack[0x30];
00883   byte position;
00884   bool used;
00885 
00886   TextRefStack() : used(false) {}
00887 
00888   TextRefStack(const TextRefStack &stack) :
00889     position(stack.position),
00890     used(stack.used)
00891   {
00892     memcpy(this->stack, stack.stack, sizeof(this->stack));
00893   }
00894 
00895   uint8  PopUnsignedByte()  { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
00896   int8   PopSignedByte()    { return (int8)this->PopUnsignedByte(); }
00897 
00898   uint16 PopUnsignedWord()
00899   {
00900     uint16 val = this->PopUnsignedByte();
00901     return val | (this->PopUnsignedByte() << 8);
00902   }
00903   int16  PopSignedWord()    { return (int32)this->PopUnsignedWord(); }
00904 
00905   uint32 PopUnsignedDWord()
00906   {
00907     uint32 val = this->PopUnsignedWord();
00908     return val | (this->PopUnsignedWord() << 16);
00909   }
00910   int32  PopSignedDWord()   { return (int32)this->PopUnsignedDWord(); }
00911 
00912   uint64 PopUnsignedQWord()
00913   {
00914     uint64 val = this->PopUnsignedDWord();
00915     return val | (((uint64)this->PopUnsignedDWord()) << 32);
00916   }
00917   int64  PopSignedQWord()   { return (int64)this->PopUnsignedQWord(); }
00918 
00920   void RotateTop4Words()
00921   {
00922     byte tmp[2];
00923     for (int i = 0; i  < 2; i++) tmp[i] = this->stack[this->position + i + 6];
00924     for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
00925     for (int i = 0; i  < 2; i++) this->stack[this->position + i] = tmp[i];
00926   }
00927 
00928   void PushWord(uint16 word)
00929   {
00930     if (this->position >= 2) {
00931       this->position -= 2;
00932     } else {
00933       for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
00934         this->stack[i] = this->stack[i - 2];
00935       }
00936     }
00937     this->stack[this->position]     = GB(word, 0, 8);
00938     this->stack[this->position + 1] = GB(word, 8, 8);
00939   }
00940 
00941   void ResetStack()  { this->position = 0; this->used = true; }
00942   void RewindStack() { this->position = 0; }
00943 };
00944 
00946 static TextRefStack _newgrf_textrefstack;
00947 
00952 bool UsingNewGRFTextStack()
00953 {
00954   return _newgrf_textrefstack.used;
00955 }
00956 
00961 struct TextRefStack *CreateTextRefStackBackup()
00962 {
00963   return new TextRefStack(_newgrf_textrefstack);
00964 }
00965 
00970 void RestoreTextRefStackBackup(struct TextRefStack *backup)
00971 {
00972   _newgrf_textrefstack = *backup;
00973   delete backup;
00974 }
00975 
00993 void StartTextRefStackUsage(byte numEntries, const uint32 *values)
00994 {
00995   extern TemporaryStorageArray<int32, 0x110> _temp_store;
00996 
00997   _newgrf_textrefstack.ResetStack();
00998 
00999   byte *p = _newgrf_textrefstack.stack;
01000   for (uint i = 0; i < numEntries; i++) {
01001     uint32 value = values != NULL ? values[i] : _temp_store.GetValue(0x100 + i);
01002     for (uint j = 0; j < 32; j += 8) {
01003       *p = GB(value, j, 8);
01004       p++;
01005     }
01006   }
01007 }
01008 
01010 void StopTextRefStackUsage()
01011 {
01012   _newgrf_textrefstack.used = false;
01013 }
01014 
01015 void RewindTextRefStack()
01016 {
01017   _newgrf_textrefstack.RewindStack();
01018 }
01019 
01029 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, bool modify_argv)
01030 {
01031   if (_newgrf_textrefstack.used && modify_argv) {
01032     switch (scc) {
01033       default: NOT_REACHED();
01034       case SCC_NEWGRF_PRINT_BYTE_SIGNED:      *argv = _newgrf_textrefstack.PopSignedByte();    break;
01035       case SCC_NEWGRF_PRINT_QWORD_CURRENCY:   *argv = _newgrf_textrefstack.PopSignedQWord();   break;
01036 
01037       case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
01038       case SCC_NEWGRF_PRINT_DWORD_SIGNED:     *argv = _newgrf_textrefstack.PopSignedDWord();   break;
01039 
01040       case SCC_NEWGRF_PRINT_BYTE_HEX:         *argv = _newgrf_textrefstack.PopUnsignedByte();  break;
01041       case SCC_NEWGRF_PRINT_QWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
01042 
01043       case SCC_NEWGRF_PRINT_WORD_SPEED:
01044       case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
01045       case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
01046       case SCC_NEWGRF_PRINT_WORD_SIGNED:      *argv = _newgrf_textrefstack.PopSignedWord();    break;
01047 
01048       case SCC_NEWGRF_PRINT_WORD_HEX:
01049       case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
01050       case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
01051       case SCC_NEWGRF_PRINT_WORD_POWER:
01052       case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
01053       case SCC_NEWGRF_PRINT_WORD_UNSIGNED:    *argv = _newgrf_textrefstack.PopUnsignedWord();  break;
01054 
01055       case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
01056       case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
01057       case SCC_NEWGRF_PRINT_DWORD_HEX:        *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
01058 
01059       case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
01060       case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:  *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
01061 
01062       case SCC_NEWGRF_DISCARD_WORD:           _newgrf_textrefstack.PopUnsignedWord(); break;
01063 
01064       case SCC_NEWGRF_ROTATE_TOP_4_WORDS:     _newgrf_textrefstack.RotateTop4Words(); break;
01065       case SCC_NEWGRF_PUSH_WORD:              _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
01066       case SCC_NEWGRF_UNPRINT:                *buff = max(*buff - Utf8Consume(str), buf_start); break;
01067 
01068       case SCC_NEWGRF_PRINT_WORD_STRING_ID:
01069         *argv = TTDPStringIDToOTTDStringIDMapping(_newgrf_textrefstack.PopUnsignedWord());
01070         break;
01071     }
01072   }
01073 
01074   switch (scc) {
01075     default: NOT_REACHED();
01076     case SCC_NEWGRF_PRINT_DWORD_SIGNED:
01077     case SCC_NEWGRF_PRINT_WORD_SIGNED:
01078     case SCC_NEWGRF_PRINT_BYTE_SIGNED:
01079     case SCC_NEWGRF_PRINT_WORD_UNSIGNED:
01080       return SCC_COMMA;
01081 
01082     case SCC_NEWGRF_PRINT_BYTE_HEX:
01083     case SCC_NEWGRF_PRINT_WORD_HEX:
01084     case SCC_NEWGRF_PRINT_DWORD_HEX:
01085     case SCC_NEWGRF_PRINT_QWORD_HEX:
01086       return SCC_HEX;
01087 
01088     case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
01089     case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
01090       return SCC_CURRENCY_LONG;
01091 
01092     case SCC_NEWGRF_PRINT_WORD_STRING_ID:
01093       return SCC_NEWGRF_PRINT_WORD_STRING_ID;
01094 
01095     case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
01096     case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
01097       return SCC_DATE_LONG;
01098 
01099     case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:
01100     case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
01101       return SCC_DATE_SHORT;
01102 
01103     case SCC_NEWGRF_PRINT_WORD_SPEED:
01104       return SCC_VELOCITY;
01105 
01106     case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
01107       return SCC_VOLUME_LONG;
01108 
01109     case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
01110       return SCC_VOLUME_SHORT;
01111 
01112     case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
01113       return SCC_WEIGHT_LONG;
01114 
01115     case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
01116       return SCC_WEIGHT_SHORT;
01117 
01118     case SCC_NEWGRF_PRINT_WORD_POWER:
01119       return SCC_POWER;
01120 
01121     case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
01122       return SCC_STATION_NAME;
01123 
01124     case SCC_NEWGRF_DISCARD_WORD:
01125     case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
01126     case SCC_NEWGRF_PUSH_WORD:
01127     case SCC_NEWGRF_UNPRINT:
01128       return 0;
01129   }
01130 }