4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
11 * @file newgrf_text.cpp
12 * Implementation of Action 04 "universal holder" structure and functions.
13 * This file implements a linked-lists of strings,
14 * holding everything that the newgrf action 04 will send over to OpenTTD.
15 * One of the biggest problems is that Dynamic lang Array uses ISO codes
16 * as way to identifying current user lang, while newgrf uses bit shift codes
17 * not related to ISO. So equivalence functionnality had to be set.
22 #include "strings_func.h"
23 #include "newgrf_storage.h"
24 #include "newgrf_text.h"
25 #include "newgrf_cargo.h"
27 #include "date_type.h"
29 #include "core/pointer.h"
30 #include "core/flexarray.h"
31 #include "core/alloc_type.hpp"
32 #include "core/smallmap_type.hpp"
35 #include "table/strings.h"
36 #include "table/control_codes.h"
39 * Explains the newgrf shift bit positioning.
40 * the grf base will not be used in order to find the string, but rather for
41 * jumping from standard langID scheme to the new one.
43 enum GRFBaseLanguages
{
44 GRFLB_AMERICAN
= 0x01,
52 enum GRFExtendedLanguages
{
53 GRFLX_AMERICAN
= 0x00,
58 GRFLX_UNSPECIFIED
= 0x7F,
63 * Holder of a GRFTextMap.
64 * Putting both grfid and stringid together allows us to avoid duplicates,
65 * since it is NOT SUPPOSED to happen.
75 static uint _num_grf_texts
= 0;
76 static GRFTextEntry _grf_text
[TAB_SIZE_NEWGRF
];
77 static byte _currentLangID
= GRFLX_ENGLISH
; ///< by default, english is used.
80 * Get the mapping from the NewGRF supplied ID to OpenTTD's internal ID.
81 * @param newgrf_id The NewGRF ID to map.
82 * @param gender Whether to map genders or cases.
83 * @return The, to OpenTTD's internal ID, mapped index, or -1 if there is no mapping.
85 int LanguageMap::GetMapping(int newgrf_id
, bool gender
) const
87 const SmallVector
<Mapping
, 1> &map
= gender
? this->gender_map
: this->case_map
;
88 for (const Mapping
*m
= map
.Begin(); m
!= map
.End(); m
++) {
89 if (m
->newgrf_id
== newgrf_id
) return m
->openttd_id
;
95 * Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
96 * @param openttd_id The OpenTTD ID to map.
97 * @param gender Whether to map genders or cases.
98 * @return The, to the NewGRF supplied ID, mapped index, or -1 if there is no mapping.
100 int LanguageMap::GetReverseMapping(int openttd_id
, bool gender
) const
102 const SmallVector
<Mapping
, 1> &map
= gender
? this->gender_map
: this->case_map
;
103 for (const Mapping
*m
= map
.Begin(); m
!= map
.End(); m
++) {
104 if (m
->openttd_id
== openttd_id
) return m
->newgrf_id
;
110 /** Allocated string with (some) built-in bounds checking. */
111 struct mstring
: stringb
, FlexArray
<char> {
115 mstring (size_t n
) : stringb (n
, this->data
)
120 static mstring
*create (size_t n
= 1)
122 return new (n
) mstring (n
);
127 /** Dump the representation of a switch case mapping. */
128 static void DumpSwitchMapping (stringb
*buf
, const LanguageMap
*lm
,
129 const ttd_unique_free_ptr
<mstring
> (&mapping
) [256])
132 * Format for case switch:
133 * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
134 * Each LEN is printed using 2 bytes in big endian order.
136 int cases
[MAX_NUM_CASES
];
138 assert (_current_language
->num_cases
<= lengthof(cases
));
142 for (uint8 i
= 0; i
< _current_language
->num_cases
; i
++) {
143 /* Count the ones we have a mapped string for. */
144 int idx
= lm
->GetReverseMapping (i
, false);
146 if ((idx
>= 0) && mapping
[idx
]) count
++;
148 buf
->append ((char)count
);
150 for (uint8 i
= 0; i
< _current_language
->num_cases
; i
++) {
151 /* Resolve the string we're looking for. */
153 if ((idx
< 0) || !mapping
[idx
]) continue;
154 const mstring
*m
= mapping
[idx
].get();
157 buf
->append ((char)(i
+ 1));
160 size_t len
= m
->length() + 1;
161 buf
->append ((char)GB(len
, 8, 8));
162 buf
->append ((char)GB(len
, 0, 8));
165 buf
->append (m
->c_str());
169 /* "<STRINGDEFAULT>" */
170 buf
->append (mapping
[0]->c_str());
174 /** Dump the representation of a choice list. */
175 static void DumpChoiceList (stringb
*buf
, const LanguageMap
*lm
,
176 const ttd_unique_free_ptr
<mstring
> (&mapping
) [256],
177 int offset
, bool gender
)
180 * Format for choice list:
181 * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
183 const mstring
*strs
[(MAX_NUM_GENDERS
> LANGUAGE_MAX_PLURAL_FORMS
) ? MAX_NUM_GENDERS
: LANGUAGE_MAX_PLURAL_FORMS
];
186 buf
->append ((char)(offset
- 0x80));
188 /* "<NUM CHOICES>" */
189 uint count
= gender
? _current_language
->num_genders
: LANGUAGE_MAX_PLURAL_FORMS
;
190 buf
->append ((char)count
);
192 assert (count
<= lengthof(strs
));
195 for (uint i
= 0; i
< count
; i
++) {
196 int idx
= gender
? lm
->GetReverseMapping (i
, true) : i
+ 1;
197 const mstring
*m
= mapping
[(idx
>= 0) && mapping
[idx
] ? idx
: 0].get();
199 size_t len
= m
->length() + 1;
201 grfmsg(1, "choice list string is too long");
204 buf
->append ((char)len
);
208 for (uint i
= 0; i
< count
; i
++) {
209 const mstring
*m
= strs
[i
];
210 /* Limit the length of the string we copy to 0xFE. The length is written above
211 * as a byte and we need room for the final '\0'. */
212 size_t len
= min
<size_t> (0xFE, m
->length());
213 buf
->append_fmt ("%.*s", (int)len
, m
->c_str());
218 /** Dump the representation of a string mapping. */
219 static void DumpMapping (stringb
*buf
, const LanguageMap
*lm
,
220 const ttd_unique_free_ptr
<mstring
> (&mapping
) [256],
221 StringControlCode type
, int offset
)
224 /* In case there is no mapping, just ignore everything but the default.
225 * A probable cause for this happening is when the language file has
226 * been removed by the user and as such no mapping could be made. */
227 buf
->append (mapping
[0]->c_str());
231 buf
->append_utf8 (type
);
234 case SCC_SWITCH_CASE
:
235 DumpSwitchMapping (buf
, lm
, mapping
);
238 case SCC_PLURAL_LIST
:
239 buf
->append ((char)lm
->plural_form
);
242 DumpChoiceList (buf
, lm
, mapping
, offset
, type
== SCC_GENDER_LIST
);
248 * Translate TTDPatch string codes into something we can handle better.
249 * @param out The string to append the result to.
250 * @param str The string to translate.
251 * @param grfid The (NewGRF) ID associated with this string
252 * @param language_id The (NewGRF) language ID associated with this string.
253 * @param allow_newlines Whether newlines are allowed in the string or not.
254 * @param byte80 The control code to use as replacement for the 0x80-value.
256 static void TranslateStringCodes (stringb
*out
, const char *str
, uint32 grfid
,
257 uint8 language_id
, bool allow_newlines
, StringControlCode byte80
)
260 CTRL_EOF
, ///< End of string.
261 CTRL_HSKIP
, ///< Variable space.
262 CTRL_NOP
, ///< Ignore.
263 CTRL_NL
, ///< New line.
264 CTRL_SETXY
, ///< Set string position.
265 CTRL_PRSTK
, ///< Print string from stack.
266 CTRL_PRSTR
, ///< Print immediate string.
267 CTRL_EXT
, ///< Extended format code.
270 assert_compile (CTRL_EXT
< 0x20);
272 static const uint16 ctrl
[0xBE] = {
273 CTRL_EOF
, CTRL_HSKIP
, '?', '?', '?', '?', '?', '?',
274 '?', '?', CTRL_NOP
, '?',
275 '?', CTRL_NL
, SCC_TINYFONT
, SCC_BIGFONT
,
276 '?', '?', '?', '?', '?', '?', '?', '?',
277 '?', '?', '?', '?', '?', '?', '?', CTRL_SETXY
,
278 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
279 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
280 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
281 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
282 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
283 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
284 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
285 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
286 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
287 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
288 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
289 0x78, 0x79, 0x7A, SCC_NEWGRF_PRINT_DWORD_SIGNED
,
290 SCC_NEWGRF_PRINT_WORD_SIGNED
, // 0x7C
291 SCC_NEWGRF_PRINT_BYTE_SIGNED
, // 0x7D
292 SCC_NEWGRF_PRINT_WORD_UNSIGNED
, // 0x7E
293 SCC_NEWGRF_PRINT_DWORD_CURRENCY
, // 0x7F
294 CTRL_PRSTK
, CTRL_PRSTR
, // 0x80, 0x81
295 SCC_NEWGRF_PRINT_WORD_DATE_LONG
, // 0x82
296 SCC_NEWGRF_PRINT_WORD_DATE_SHORT
, // 0x83
297 SCC_NEWGRF_PRINT_WORD_SPEED
, // 0x84
298 SCC_NEWGRF_DISCARD_WORD
, // 0x85
299 SCC_NEWGRF_ROTATE_TOP_4_WORDS
, // 0x86
300 SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
, // 0x87
301 SCC_BLUE
, SCC_SILVER
, SCC_GOLD
, SCC_RED
, // 0x88
302 SCC_PURPLE
, SCC_LTBROWN
, SCC_ORANGE
, SCC_GREEN
,
303 SCC_YELLOW
, SCC_DKGREEN
, SCC_CREAM
, SCC_BROWN
, // 0x90
304 SCC_WHITE
, SCC_LTBLUE
, SCC_GRAY
, SCC_DKBLUE
,
305 SCC_BLACK
, 0x99, CTRL_EXT
, 0x9B, 0x9C, 0x9D, 0x20AC, 0x0178,
306 SCC_UP_ARROW
, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
307 0xA8, 0xA9, SCC_DOWN_ARROW
, 0xAB,
308 SCC_CHECKMARK
, SCC_CROSS
, 0xAE, SCC_RIGHT_ARROW
,
309 0xB0, 0xB1, 0xB2, 0xB3,
310 SCC_TRAIN
, SCC_LORRY
, SCC_BUS
, SCC_PLANE
,
311 SCC_SHIP
, SCC_SUPERSCRIPT_M1
, 0xBA, 0xBB,
312 SCC_SMALL_UP_ARROW
, SCC_SMALL_DOWN_ARROW
,
317 /* Helper variables for a possible (string) mapping. */
318 StringControlCode mapping_type
= (StringControlCode
)0;
321 /* Mapping of NewGRF-supplied ID to the different strings in the choice list. */
322 ttd_unique_free_ptr
<mstring
> mapping_strings
[256];
324 bool unicode
= skip_nfo_utf8_identifier (&str
);
328 bool try_control
= true;
329 if (unicode
&& Utf8EncodedCharLen(*str
) != 0) {
330 c
= Utf8Consume(&str
);
331 /* 'Magic' range of control codes. */
332 if (GB(c
, 8, 8) == 0xE0) {
334 } else if (c
>= 0x20) {
341 if (try_control
&& (c
< lengthof(ctrl
))) {
343 } else if (!IsPrintable (c
)) {
352 if (str
[0] == '\0') goto string_end
;
361 if (allow_newlines
) {
364 grfmsg(1, "Detected newline in string that does not allow one");
369 if (str
[0] == '\0' || str
[1] == '\0') goto string_end
;
375 buf
->append_utf8 (byte80
);
379 if (str
[0] == '\0' || str
[1] == '\0') goto string_end
;
381 string
= ((uint8
)*str
++);
382 string
|= ((uint8
)*str
++) << 8;
383 buf
->append_utf8 (SCC_NEWGRF_STRINL
);
384 buf
->append_utf8 (MapGRFStringID (grfid
, string
));
391 case 0x00: goto string_end
;
392 case 0x01: buf
->append_utf8 (SCC_NEWGRF_PRINT_QWORD_CURRENCY
); break;
393 /* 0x02: ignore next colour byte is not supported. It works on the final
394 * string and as such hooks into the string drawing routine. At that
395 * point many things already happened, such as splitting up of strings
396 * when drawn over multiple lines or right-to-left translations, which
397 * make the behaviour peculiar, e.g. only happening at specific width
398 * of windows. Or we need to add another pass over the string to just
399 * support this. As such it is not implemented in OpenTTD. */
401 if (str
[0] == '\0' || str
[1] == '\0') goto string_end
;
402 uint16 tmp
= ((uint8
)*str
++);
403 tmp
|= ((uint8
)*str
++) << 8;
404 buf
->append_utf8 (SCC_NEWGRF_PUSH_WORD
);
405 buf
->append_utf8 (tmp
);
409 if (str
[0] == '\0') goto string_end
;
410 buf
->append_utf8 (SCC_NEWGRF_UNPRINT
);
411 buf
->append_utf8 (*str
++);
413 case 0x06: buf
->append_utf8 (SCC_NEWGRF_PRINT_BYTE_HEX
); break;
414 case 0x07: buf
->append_utf8 (SCC_NEWGRF_PRINT_WORD_HEX
); break;
415 case 0x08: buf
->append_utf8 (SCC_NEWGRF_PRINT_DWORD_HEX
); break;
416 /* 0x09, 0x0A are TTDPatch internal use only string codes. */
417 case 0x0B: buf
->append_utf8 (SCC_NEWGRF_PRINT_QWORD_HEX
); break;
418 case 0x0C: buf
->append_utf8 (SCC_NEWGRF_PRINT_WORD_STATION_NAME
); break;
419 case 0x0D: buf
->append_utf8 (SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
); break;
422 if (str
[0] == '\0') goto string_end
;
423 const LanguageMap
*lm
= LanguageMap::GetLanguageMap(grfid
, language_id
);
425 int mapped
= lm
!= NULL
? lm
->GetMapping(index
, code
== 0x0E) : -1;
427 buf
->append_utf8 (code
== 0x0E ? SCC_GENDER_INDEX
: SCC_SET_CASE
);
428 buf
->append_utf8 (code
== 0x0E ? mapped
: mapped
+ 1);
435 if (str
[0] == '\0') goto string_end
;
436 if (mapping_type
== 0) {
437 if (code
== 0x10) str
++; // Skip the index
438 grfmsg(1, "choice list %s marker found when not expected", code
== 0x10 ? "next" : "default");
441 /* Terminate the previous string. */
442 byte index
= (code
== 0x10 ? *str
++ : 0);
443 if (mapping_strings
[index
]) {
444 grfmsg(1, "duplicate choice list string, ignoring");
447 mstring
*m
= mstring::create (strlen(str
) * 10 + 1);
448 mapping_strings
[index
].reset (m
);
455 if (mapping_type
== 0) {
456 grfmsg(1, "choice list end marker found when not expected");
458 /* Terminate the previous string. */
461 if (!mapping_strings
[0]) {
462 /* In case of a (broken) NewGRF without a default,
463 * assume an empty string. */
464 grfmsg(1, "choice list misses default value");
465 mapping_strings
[0].reset (mstring::create());
468 /* Now we can start flushing everything and clean everything up. */
469 const LanguageMap
*lm
= LanguageMap::GetLanguageMap (grfid
, language_id
);
470 DumpMapping (buf
, lm
, mapping_strings
, mapping_type
, mapping_offset
);
472 mapping_type
= (StringControlCode
)0;
473 for (uint i
= 0; i
< lengthof(mapping_strings
); i
++) {
474 mapping_strings
[i
].reset();
482 if (str
[0] == '\0') goto string_end
;
483 if (mapping_type
!= 0) {
484 grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
485 if (code
!= 0x14) str
++;
487 static const StringControlCode mp
[] = { SCC_GENDER_LIST
, SCC_SWITCH_CASE
, SCC_PLURAL_LIST
};
488 mapping_type
= mp
[code
- 0x13];
489 if (code
!= 0x14) mapping_offset
= *str
++;
502 buf
->append_utf8 (SCC_NEWGRF_PRINT_DWORD_DATE_LONG
+ code
- 0x16);
506 grfmsg(1, "missing handler for extended format code");
513 buf
->append_utf8 (c
);
519 if (mapping_type
!= 0) {
520 grfmsg(1, "choice list was incomplete, the whole list is ignored");
526 * Allocate and assign a new GRFText with the given text,
527 * translating string codes.
528 * @param text The text to store in the new GRFText.
529 * @param grfid The grfid where this string is defined.
530 * @param langid The language of the new text.
531 * @param allow_newlines Whether newlines are allowed in this string.
533 GRFText
*GRFText::create (const char *text
, uint32 grfid
, byte langid
,
536 size_t tmp_len
= strlen (text
) * 10 + 1; // Allocate space to allow for expansion
537 char *tmp
= xmalloc (tmp_len
);
538 stringb
tmp_buf (tmp_len
, tmp
);
540 TranslateStringCodes (&tmp_buf
, text
, grfid
, langid
, allow_newlines
,
541 SCC_NEWGRF_PRINT_WORD_STRING_ID
);
543 size_t len
= tmp_buf
.length() + 1;
544 GRFText
*newtext
= GRFText::create (tmp
, len
);
550 /** Construct a copy of this text map. */
551 GRFTextMap::GRFTextMap (const GRFTextMap
&other
)
552 : std::map
<byte
, GRFText
*> (other
)
554 for (iterator iter
= this->begin(); iter
!= this->end(); iter
++) {
555 iter
->second
= iter
->second
->clone();
559 /** Destroy the text map. */
560 GRFTextMap::~GRFTextMap()
562 for (iterator iter
= this->begin(); iter
!= this->end(); iter
++) {
567 /** Get the GRFText for the current language, or a default. */
568 const GRFText
*GRFTextMap::get_current (void) const
570 byte langs
[4] = { _currentLangID
, GRFLX_UNSPECIFIED
, GRFLX_ENGLISH
, GRFLX_AMERICAN
};
572 for (uint i
= 0; i
< lengthof(langs
); i
++) {
573 const_iterator iter
= this->find (langs
[i
]);
574 if (iter
!= this->end()) return iter
->second
;
580 /** Add a GRFText to this map. */
581 void GRFTextMap::add (byte langid
, GRFText
*text
)
583 std::pair
<iterator
, bool> pair
=
584 this->insert (std::make_pair (langid
, text
));
587 /* The langid already existed in the map. */
588 GRFText
*old
= pair
.first
->second
;
589 pair
.first
->second
= text
;
595 * Add a string to this map.
596 * @param langid The language of the new text.
597 * @param grfid The grfid where this string is defined.
598 * @param allow_newlines Whether newlines are allowed in this string.
599 * @param text The text to add to the list.
600 * @note All text-codes will be translated.
602 void GRFTextMap::add (byte langid
, uint32 grfid
, bool allow_newlines
, const char *text
)
604 this->add (langid
, GRFText::create (text
, grfid
, langid
, allow_newlines
));
609 * Translate TTDPatch string codes into something OpenTTD can handle (better).
610 * @param grfid The (NewGRF) ID associated with this string
611 * @param language_id The (NewGRF) language ID associated with this string.
612 * @param allow_newlines Whether newlines are allowed in the string or not.
613 * @param str The string to translate.
614 * @param byte80 The control code to use as replacement for the 0x80-value.
615 * @return The translated string.
617 char *TranslateTTDPatchCodes (uint32 grfid
, uint8 language_id
,
618 bool allow_newlines
, const char *str
, StringControlCode byte80
)
620 size_t tmp_len
= strlen (str
) * 10 + 1; // Allocate space to allow for expansion
621 char *tmp
= xmalloc (tmp_len
);
622 stringb
tmp_buf (tmp_len
, tmp
);
624 TranslateStringCodes (&tmp_buf
, str
, grfid
, language_id
,
625 allow_newlines
, byte80
);
627 return xrealloc (tmp
, tmp_buf
.length() + 1);
631 * Add the new read string into our structure.
633 static StringID
AddGRFString (uint textid
, byte langid
, const char *text
,
636 GRFTextEntry
*entry
= &_grf_text
[textid
];
637 GRFText
*newtext
= GRFText::create (text
, entry
->grfid
,
638 langid
, allow_newlines
);
639 entry
->map
->add (langid
, newtext
);
641 StringID str
= MakeStringID (TEXT_TAB_NEWGRF_START
, textid
);
643 grfmsg (3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s' (%X)",
644 textid
, entry
->grfid
, entry
->stringid
, langid
,
650 /** Find a GRF string entry. */
651 static uint
FindGRFStringEntry (uint32 grfid
, uint16 stringid
)
653 for (uint id
= 0; id
< _num_grf_texts
; id
++) {
654 if (_grf_text
[id
].grfid
== grfid
&& _grf_text
[id
].stringid
== stringid
) {
658 return _num_grf_texts
;
662 * Add the new read string into our structure.
664 StringID
AddGRFString(uint32 grfid
, uint16 stringid
, byte langid_to_add
, bool new_scheme
, bool allow_newlines
, const char *text_to_add
, StringID def_string
)
666 uint id
= FindGRFStringEntry (grfid
, stringid
);
668 /* Too many strings allocated, return empty */
669 if (id
== lengthof(_grf_text
)) return STR_EMPTY
;
671 /* If we didn't find our stringid and grfid in the list, allocate a new id */
672 if (id
== _num_grf_texts
) _num_grf_texts
++;
674 if (_grf_text
[id
].map
== NULL
) {
675 _grf_text
[id
].grfid
= grfid
;
676 _grf_text
[id
].stringid
= stringid
;
677 _grf_text
[id
].def_string
= def_string
;
678 _grf_text
[id
].map
= new GRFTextMap
;
681 /* When working with the old language scheme (grf_version is less
682 * than 7) and English or American is among the set bits, simply
683 * add it as English in the new scheme, i.e. as langid = 1.
684 * If English is set, it is pretty safe to assume the translations
685 * are not actually translated.
688 if (langid_to_add
& (GRFLB_AMERICAN
| GRFLB_ENGLISH
)) {
689 langid_to_add
= GRFLX_ENGLISH
;
691 StringID ret
= STR_EMPTY
;
692 if (langid_to_add
& GRFLB_GERMAN
) ret
= AddGRFString (id
, GRFLX_GERMAN
, text_to_add
, allow_newlines
);
693 if (langid_to_add
& GRFLB_FRENCH
) ret
= AddGRFString (id
, GRFLX_FRENCH
, text_to_add
, allow_newlines
);
694 if (langid_to_add
& GRFLB_SPANISH
) ret
= AddGRFString (id
, GRFLX_SPANISH
, text_to_add
, allow_newlines
);
699 return AddGRFString (id
, langid_to_add
, text_to_add
, allow_newlines
);
703 * Returns the index for this stringid associated with its grfID
705 StringID
GetGRFStringID(uint32 grfid
, StringID stringid
)
707 uint id
= FindGRFStringEntry (grfid
, stringid
);
708 return (id
== _num_grf_texts
) ? STR_UNDEFINED
:
709 MakeStringID (TEXT_TAB_NEWGRF_START
, id
);
714 * Get a C-string from a stringid set by a newgrf.
716 const char *GetGRFStringPtr(uint16 stringid
)
718 assert(_grf_text
[stringid
].grfid
!= 0);
720 const char *str
= _grf_text
[stringid
].map
->get_string();
721 if (str
!= NULL
) return str
;
723 /* Use the default string ID if the fallback string isn't available */
724 return GetStringPtr(_grf_text
[stringid
].def_string
);
728 * Equivalence Setter function between game and newgrf langID.
729 * This function will adjust _currentLangID as to what is the LangID
730 * of the current language set by the user.
731 * This function is called after the user changed language,
732 * from strings.cpp:ReadLanguagePack
733 * @param language_id iso code of current selection
735 void SetCurrentGrfLangID(byte language_id
)
737 _currentLangID
= language_id
;
740 bool CheckGrfLangID(byte lang_id
, byte grf_version
)
742 if (grf_version
< 7) {
743 switch (_currentLangID
) {
744 case GRFLX_GERMAN
: return (lang_id
& GRFLB_GERMAN
) != 0;
745 case GRFLX_FRENCH
: return (lang_id
& GRFLB_FRENCH
) != 0;
746 case GRFLX_SPANISH
: return (lang_id
& GRFLB_SPANISH
) != 0;
747 default: return (lang_id
& (GRFLB_ENGLISH
| GRFLB_AMERICAN
)) != 0;
751 return (lang_id
== _currentLangID
|| lang_id
== GRFLX_UNSPECIFIED
);
756 * Remove all strings and reset the text counter.
758 void CleanUpStrings()
762 for (id
= 0; id
< _num_grf_texts
; id
++) {
763 delete _grf_text
[id
].map
;
764 _grf_text
[id
].grfid
= 0;
765 _grf_text
[id
].stringid
= 0;
766 _grf_text
[id
].map
= NULL
;
772 struct TextRefStack
{
775 const GRFFile
*grffile
;
778 TextRefStack() : position(0), grffile(NULL
), used(false) {}
780 TextRefStack(const TextRefStack
&stack
) :
781 position(stack
.position
),
782 grffile(stack
.grffile
),
785 memcpy(this->stack
, stack
.stack
, sizeof(this->stack
));
788 uint8
PopUnsignedByte() { assert(this->position
< lengthof(this->stack
)); return this->stack
[this->position
++]; }
789 int8
PopSignedByte() { return (int8
)this->PopUnsignedByte(); }
791 uint16
PopUnsignedWord()
793 uint16 val
= this->PopUnsignedByte();
794 return val
| (this->PopUnsignedByte() << 8);
796 int16
PopSignedWord() { return (int32
)this->PopUnsignedWord(); }
798 uint32
PopUnsignedDWord()
800 uint32 val
= this->PopUnsignedWord();
801 return val
| (this->PopUnsignedWord() << 16);
803 int32
PopSignedDWord() { return (int32
)this->PopUnsignedDWord(); }
805 uint64
PopUnsignedQWord()
807 uint64 val
= this->PopUnsignedDWord();
808 return val
| (((uint64
)this->PopUnsignedDWord()) << 32);
810 int64
PopSignedQWord() { return (int64
)this->PopUnsignedQWord(); }
812 /** Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3 */
813 void RotateTop4Words()
816 for (int i
= 0; i
< 2; i
++) tmp
[i
] = this->stack
[this->position
+ i
+ 6];
817 for (int i
= 5; i
>= 0; i
--) this->stack
[this->position
+ i
+ 2] = this->stack
[this->position
+ i
];
818 for (int i
= 0; i
< 2; i
++) this->stack
[this->position
+ i
] = tmp
[i
];
821 void PushWord(uint16 word
)
823 if (this->position
>= 2) {
826 for (int i
= lengthof(stack
) - 1; i
>= this->position
+ 2; i
--) {
827 this->stack
[i
] = this->stack
[i
- 2];
830 this->stack
[this->position
] = GB(word
, 0, 8);
831 this->stack
[this->position
+ 1] = GB(word
, 8, 8);
834 void ResetStack(const GRFFile
*grffile
)
836 assert(grffile
!= NULL
);
838 this->grffile
= grffile
;
842 void RewindStack() { this->position
= 0; }
845 /** The stack that is used for TTDP compatible string code parsing */
846 static TextRefStack _newgrf_textrefstack
;
849 * Check whether the NewGRF text stack is in use.
850 * @return True iff the NewGRF text stack is used.
852 bool UsingNewGRFTextStack()
854 return _newgrf_textrefstack
.used
;
858 * Create a backup of the current NewGRF text stack.
859 * @return A copy of the current text stack.
861 struct TextRefStack
*CreateTextRefStackBackup()
863 return new TextRefStack(_newgrf_textrefstack
);
867 * Restore a copy of the text stack to the used stack.
868 * @param backup The copy to restore.
870 void RestoreTextRefStackBackup(struct TextRefStack
*backup
)
872 _newgrf_textrefstack
= *backup
;
877 * Start using the TTDP compatible string code parsing.
879 * On start a number of values is copied on the #TextRefStack.
880 * You can then use #GetString() and the normal string drawing functions,
881 * and they will use the #TextRefStack for NewGRF string codes.
883 * However, when you want to draw a string multiple times using the same stack,
884 * you have to call #RewindTextRefStack() between draws.
886 * After you are done with drawing, you must disable usage of the #TextRefStack
887 * by calling #StopTextRefStackUsage(), so NewGRF string codes operate on the
888 * normal string parameters again.
890 * @param grffile the NewGRF providing the stack data
891 * @param numEntries number of entries to copy from the registers
892 * @param values values to copy onto the stack; if NULL the temporary NewGRF registers will be used instead
894 void StartTextRefStackUsage(const GRFFile
*grffile
, byte numEntries
, const uint32
*values
)
896 extern TemporaryStorageArray
<int32
, 0x110> _temp_store
;
898 _newgrf_textrefstack
.ResetStack(grffile
);
900 byte
*p
= _newgrf_textrefstack
.stack
;
901 for (uint i
= 0; i
< numEntries
; i
++) {
902 uint32 value
= values
!= NULL
? values
[i
] : _temp_store
.GetValue(0x100 + i
);
903 for (uint j
= 0; j
< 32; j
+= 8) {
904 *p
= GB(value
, j
, 8);
910 /** Stop using the TTDP compatible string code parsing */
911 void StopTextRefStackUsage()
913 _newgrf_textrefstack
.used
= false;
916 void RewindTextRefStack()
918 _newgrf_textrefstack
.RewindStack();
922 * FormatString for NewGRF specific "magic" string control codes
923 * @param scc the string control code that has been read
924 * @param buf the buffer we're writing to
925 * @param str the string that we need to write
926 * @param argv the OpenTTD stack of values
927 * @param argv_size space on the stack \a argv
928 * @param modify_argv When true, modify the OpenTTD stack.
929 * @return the string control code to "execute" now
931 uint
RemapNewGRFStringControlCode (uint scc
, stringb
*buf
, const char **str
, int64
*argv
, uint argv_size
, bool modify_argv
)
936 case SCC_NEWGRF_PRINT_DWORD_SIGNED
:
937 case SCC_NEWGRF_PRINT_WORD_SIGNED
:
938 case SCC_NEWGRF_PRINT_BYTE_SIGNED
:
939 case SCC_NEWGRF_PRINT_WORD_UNSIGNED
:
940 case SCC_NEWGRF_PRINT_BYTE_HEX
:
941 case SCC_NEWGRF_PRINT_WORD_HEX
:
942 case SCC_NEWGRF_PRINT_DWORD_HEX
:
943 case SCC_NEWGRF_PRINT_QWORD_HEX
:
944 case SCC_NEWGRF_PRINT_DWORD_CURRENCY
:
945 case SCC_NEWGRF_PRINT_QWORD_CURRENCY
:
946 case SCC_NEWGRF_PRINT_WORD_STRING_ID
:
947 case SCC_NEWGRF_PRINT_WORD_DATE_LONG
:
948 case SCC_NEWGRF_PRINT_DWORD_DATE_LONG
:
949 case SCC_NEWGRF_PRINT_WORD_DATE_SHORT
:
950 case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
:
951 case SCC_NEWGRF_PRINT_WORD_SPEED
:
952 case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
:
953 case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
:
954 case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
:
955 case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
:
956 case SCC_NEWGRF_PRINT_WORD_POWER
:
957 case SCC_NEWGRF_PRINT_WORD_STATION_NAME
:
958 case SCC_NEWGRF_PRINT_WORD_CARGO_NAME
:
960 DEBUG(misc
, 0, "Too many NewGRF string parameters.");
965 case SCC_NEWGRF_PRINT_WORD_CARGO_LONG
:
966 case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
:
967 case SCC_NEWGRF_PRINT_WORD_CARGO_TINY
:
969 DEBUG(misc
, 0, "Too many NewGRF string parameters.");
975 if (_newgrf_textrefstack
.used
&& modify_argv
) {
977 default: NOT_REACHED();
978 case SCC_NEWGRF_PRINT_BYTE_SIGNED
: *argv
= _newgrf_textrefstack
.PopSignedByte(); break;
979 case SCC_NEWGRF_PRINT_QWORD_CURRENCY
: *argv
= _newgrf_textrefstack
.PopSignedQWord(); break;
981 case SCC_NEWGRF_PRINT_DWORD_CURRENCY
:
982 case SCC_NEWGRF_PRINT_DWORD_SIGNED
: *argv
= _newgrf_textrefstack
.PopSignedDWord(); break;
984 case SCC_NEWGRF_PRINT_BYTE_HEX
: *argv
= _newgrf_textrefstack
.PopUnsignedByte(); break;
985 case SCC_NEWGRF_PRINT_QWORD_HEX
: *argv
= _newgrf_textrefstack
.PopUnsignedQWord(); break;
987 case SCC_NEWGRF_PRINT_WORD_SPEED
:
988 case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
:
989 case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
:
990 case SCC_NEWGRF_PRINT_WORD_SIGNED
: *argv
= _newgrf_textrefstack
.PopSignedWord(); break;
992 case SCC_NEWGRF_PRINT_WORD_HEX
:
993 case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
:
994 case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
:
995 case SCC_NEWGRF_PRINT_WORD_POWER
:
996 case SCC_NEWGRF_PRINT_WORD_STATION_NAME
:
997 case SCC_NEWGRF_PRINT_WORD_UNSIGNED
: *argv
= _newgrf_textrefstack
.PopUnsignedWord(); break;
999 case SCC_NEWGRF_PRINT_DWORD_DATE_LONG
:
1000 case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
:
1001 case SCC_NEWGRF_PRINT_DWORD_HEX
: *argv
= _newgrf_textrefstack
.PopUnsignedDWord(); break;
1003 case SCC_NEWGRF_PRINT_WORD_DATE_LONG
:
1004 case SCC_NEWGRF_PRINT_WORD_DATE_SHORT
: *argv
= _newgrf_textrefstack
.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR
; break;
1006 case SCC_NEWGRF_DISCARD_WORD
: _newgrf_textrefstack
.PopUnsignedWord(); break;
1008 case SCC_NEWGRF_ROTATE_TOP_4_WORDS
: _newgrf_textrefstack
.RotateTop4Words(); break;
1009 case SCC_NEWGRF_PUSH_WORD
: _newgrf_textrefstack
.PushWord(Utf8Consume(str
)); break;
1011 case SCC_NEWGRF_UNPRINT
: {
1012 size_t unprint
= Utf8Consume(str
);
1013 if (unprint
< buf
->length()) {
1014 buf
->truncate (buf
->length() - unprint
);
1021 case SCC_NEWGRF_PRINT_WORD_CARGO_LONG
:
1022 case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
:
1023 case SCC_NEWGRF_PRINT_WORD_CARGO_TINY
:
1024 argv
[0] = GetCargoTranslation(_newgrf_textrefstack
.PopUnsignedWord(), _newgrf_textrefstack
.grffile
);
1025 argv
[1] = _newgrf_textrefstack
.PopUnsignedWord();
1028 case SCC_NEWGRF_PRINT_WORD_STRING_ID
:
1029 *argv
= MapGRFStringID(_newgrf_textrefstack
.grffile
->grfid
, _newgrf_textrefstack
.PopUnsignedWord());
1032 case SCC_NEWGRF_PRINT_WORD_CARGO_NAME
: {
1033 CargoID cargo
= GetCargoTranslation(_newgrf_textrefstack
.PopUnsignedWord(), _newgrf_textrefstack
.grffile
);
1034 *argv
= cargo
< NUM_CARGO
? 1 << cargo
: 0;
1039 /* Consume additional parameter characters */
1043 case SCC_NEWGRF_PUSH_WORD
:
1044 case SCC_NEWGRF_UNPRINT
:
1051 default: NOT_REACHED();
1052 case SCC_NEWGRF_PRINT_DWORD_SIGNED
:
1053 case SCC_NEWGRF_PRINT_WORD_SIGNED
:
1054 case SCC_NEWGRF_PRINT_BYTE_SIGNED
:
1055 case SCC_NEWGRF_PRINT_WORD_UNSIGNED
:
1058 case SCC_NEWGRF_PRINT_BYTE_HEX
:
1059 case SCC_NEWGRF_PRINT_WORD_HEX
:
1060 case SCC_NEWGRF_PRINT_DWORD_HEX
:
1061 case SCC_NEWGRF_PRINT_QWORD_HEX
:
1064 case SCC_NEWGRF_PRINT_DWORD_CURRENCY
:
1065 case SCC_NEWGRF_PRINT_QWORD_CURRENCY
:
1066 return SCC_CURRENCY_LONG
;
1068 case SCC_NEWGRF_PRINT_WORD_STRING_ID
:
1069 return SCC_NEWGRF_PRINT_WORD_STRING_ID
;
1071 case SCC_NEWGRF_PRINT_WORD_DATE_LONG
:
1072 case SCC_NEWGRF_PRINT_DWORD_DATE_LONG
:
1073 return SCC_DATE_LONG
;
1075 case SCC_NEWGRF_PRINT_WORD_DATE_SHORT
:
1076 case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
:
1077 return SCC_DATE_SHORT
;
1079 case SCC_NEWGRF_PRINT_WORD_SPEED
:
1080 return SCC_VELOCITY
;
1082 case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
:
1083 return SCC_VOLUME_LONG
;
1085 case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
:
1086 return SCC_VOLUME_SHORT
;
1088 case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
:
1089 return SCC_WEIGHT_LONG
;
1091 case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
:
1092 return SCC_WEIGHT_SHORT
;
1094 case SCC_NEWGRF_PRINT_WORD_POWER
:
1097 case SCC_NEWGRF_PRINT_WORD_CARGO_LONG
:
1098 return SCC_CARGO_LONG
;
1100 case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
:
1101 return SCC_CARGO_SHORT
;
1103 case SCC_NEWGRF_PRINT_WORD_CARGO_TINY
:
1104 return SCC_CARGO_TINY
;
1106 case SCC_NEWGRF_PRINT_WORD_CARGO_NAME
:
1107 return SCC_CARGO_LIST
;
1109 case SCC_NEWGRF_PRINT_WORD_STATION_NAME
:
1110 return SCC_STATION_NAME
;
1112 case SCC_NEWGRF_DISCARD_WORD
:
1113 case SCC_NEWGRF_ROTATE_TOP_4_WORDS
:
1114 case SCC_NEWGRF_PUSH_WORD
:
1115 case SCC_NEWGRF_UNPRINT
: