/** * Draw an unformatted news message truncated to a maximum length. If * length exceeds maximum length it will be postfixed by '...' * @param left the left most location for the string * @param right the right most location for the string * @param y position of the string * @param colour the colour the string will be shown in * @param *ni NewsItem being printed * @param maxw maximum width of string in pixels */ static void DrawNewsString(uint left, uint right, int y, TextColour colour, const NewsItem *ni) { char buffer[512], buffer2[512]; StringID str; CopyInDParam(0, ni->params, lengthof(ni->params)); str = ni->string_id; GetString(buffer, str, lastof(buffer)); /* Copy the just gotten string to another buffer to remove any formatting * from it such as big fonts, etc. */ const char *ptr = buffer; char *dest = buffer2; WChar c_last = '\0'; for (;;) { WChar c = Utf8Consume(&ptr); if (c == 0) break; /* Make a space from a newline, but ignore multiple newlines */ if (c == '\n' && c_last != '\n') { dest[0] = ' '; dest++; } else if (c == '\r') { dest[0] = dest[1] = dest[2] = dest[3] = ' '; dest += 4; } else if (IsPrintable(c)) { dest += Utf8Encode(dest, c); } c_last = c; } *dest = '\0'; /* Truncate and show string; postfixed by '...' if necessary */ DrawString(left, right, y, buffer2, colour); }
/** * Scan the string for old values of SCC_ENCODED and fix it to * it's new, static value. * @param str the string to scan * @param last the last valid character of str */ void str_fix_scc_encoded(char *str, const char *last) { while (str <= last && *str != '\0') { size_t len = Utf8EncodedCharLen(*str); if ((len == 0 && str + 4 > last) || str + len > last) break; WChar c; len = Utf8Decode(&c, str); if (c == '\0') break; if (c == 0xE028 || c == 0xE02A) { c = SCC_ENCODED; } str += Utf8Encode(str, c); } *str = '\0'; }
/** * Insert a character to a textbuffer. If maxwidth of the Textbuf is zero, * we don't care about the visual-length but only about the physical * length of the string * @param key Character to be inserted * @return Return true on successful change of Textbuf, or false otherwise */ bool Textbuf::InsertChar(WChar key) { uint16 len = (uint16)Utf8CharLen(key); if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) { memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos); Utf8Encode(this->buf + this->caretpos, key); this->chars++; this->bytes += len; this->caretpos += len; this->UpdateStringIter(); this->UpdateWidth(); this->UpdateCaretPosition(); this->UpdateMarkedText(); return true; } return false; }
/** * Copy and convert old custom names to UTF-8. * They were all stored in a 512 by 32 (200 by 24 for TTO) long string array * and are now stored with stations, waypoints and other places with names. * @param id the StringID of the custom name to clone. * @return the clones custom name. */ char *CopyFromOldName(StringID id) { /* Is this name an (old) custom name? */ if (GB(id, 11, 5) != 15) return NULL; if (CheckSavegameVersion(37)) { /* Old names were 24/32 characters long, so 128 characters should be * plenty to allow for expansion when converted to UTF-8. */ char tmp[128]; uint offs = _savegame_type == SGT_TTO ? 24 * GB(id, 0, 8) : 32 * GB(id, 0, 9); const char *strfrom = &_old_name_array[offs]; char *strto = tmp; for (; *strfrom != '\0'; strfrom++) { WChar c = (byte)*strfrom; /* Map from non-ISO8859-15 characters to UTF-8. */ switch (c) { case 0xA4: c = 0x20AC; break; // Euro case 0xA6: c = 0x0160; break; // S with caron case 0xA8: c = 0x0161; break; // s with caron case 0xB4: c = 0x017D; break; // Z with caron case 0xB8: c = 0x017E; break; // z with caron case 0xBC: c = 0x0152; break; // OE ligature case 0xBD: c = 0x0153; break; // oe ligature case 0xBE: c = 0x0178; break; // Y with diaresis default: break; } /* Check character will fit into our buffer. */ if (strto + Utf8CharLen(c) > lastof(tmp)) break; strto += Utf8Encode(strto, c); } /* Terminate the new string and copy it back to the name array */ *strto = '\0'; return strdup(tmp); } else { /* Name will already be in UTF-8. */ return strdup(&_old_name_array[32 * GB(id, 0, 9)]); } }
/** * Copy and convert old custom names to UTF-8. * They were all stored in a 512 by 32 (200 by 24 for TTO) long string array * and are now stored with stations, waypoints and other places with names. * @param id the StringID of the custom name to clone. * @return the clones custom name. */ char *CopyFromOldName(StringID id) { /* Is this name an (old) custom name? */ if (GetStringTab(id) != TEXT_TAB_OLD_CUSTOM) return NULL; if (IsSavegameVersionBefore(SLV_37)) { /* Allow for expansion when converted to UTF-8. */ char tmp[LEN_OLD_STRINGS * MAX_CHAR_LENGTH]; uint offs = _savegame_type == SGT_TTO ? LEN_OLD_STRINGS_TTO * GB(id, 0, 8) : LEN_OLD_STRINGS * GB(id, 0, 9); const char *strfrom = &_old_name_array[offs]; char *strto = tmp; for (; *strfrom != '\0'; strfrom++) { WChar c = (byte)*strfrom; /* Map from non-ISO8859-15 characters to UTF-8. */ switch (c) { case 0xA4: c = 0x20AC; break; // Euro case 0xA6: c = 0x0160; break; // S with caron case 0xA8: c = 0x0161; break; // s with caron case 0xB4: c = 0x017D; break; // Z with caron case 0xB8: c = 0x017E; break; // z with caron case 0xBC: c = 0x0152; break; // OE ligature case 0xBD: c = 0x0153; break; // oe ligature case 0xBE: c = 0x0178; break; // Y with diaresis default: break; } /* Check character will fit into our buffer. */ if (strto + Utf8CharLen(c) > lastof(tmp)) break; strto += Utf8Encode(strto, c); } /* Terminate the new string and copy it back to the name array */ *strto = '\0'; return stredup(tmp); } else { /* Name will already be in UTF-8. */ return stredup(&_old_name_array[LEN_OLD_STRINGS * GB(id, 0, 9)]); } }
char *ScriptText::_GetEncodedText(char *p, char *lastofp) { p += Utf8Encode(p, SCC_ENCODED); p += seprintf(p, lastofp, "%X", this->string); for (int i = 0; i < this->paramc; i++) { if (this->params[i] != NULL) { p += seprintf(p, lastofp, ":\"%s\"", this->params[i]); continue; } if (this->paramt[i] != NULL) { p += seprintf(p, lastofp, ":"); p = this->paramt[i]->_GetEncodedText(p, lastofp); continue; } p += seprintf(p, lastofp,":%X", this->parami[i]); } return p; }
static bool DrawScrollingStatusText(const NewsItem *ni, int scroll_pos, int left, int right, int top, int bottom) { CopyInDParam(0, ni->params, lengthof(ni->params)); StringID str = ni->string_id; char buf[512]; GetString(buf, str, lastof(buf)); const char *s = buf; char buffer[256]; char *d = buffer; const char *last = lastof(buffer); for (;;) { WChar c = Utf8Consume(&s); if (c == 0) { break; } else if (c == '\n') { if (d + 4 >= last) break; d[0] = d[1] = d[2] = d[3] = ' '; d += 4; } else if (IsPrintable(c)) { if (d + Utf8CharLen(c) >= last) break; d += Utf8Encode(d, c); } } *d = '\0'; DrawPixelInfo tmp_dpi; if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left, bottom)) return true; int width = GetStringBoundingBox(buffer).width; int pos = (_current_text_dir == TD_RTL) ? (scroll_pos - width) : (right - scroll_pos - left); DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &tmp_dpi; DrawString(pos, INT16_MAX, 0, buffer, TC_LIGHT_BLUE, SA_LEFT | SA_FORCE); _cur_dpi = old_dpi; return (_current_text_dir == TD_RTL) ? (pos < right - left) : (pos + width > 0); }
/** * Convert to OpenTTD's encoding from that of the local environment. * When the project is built in UNICODE, the system codepage is irrelevant and * the input string is wide. In ANSI mode, the string is in the * local codepage which we'll convert to wide-char, and then to UTF-8. * OpenTTD internal encoding is UTF8. * The returned value's contents can only be guaranteed until the next call to * this function. So if the value is needed for anything else, use convert_from_fs * @param name pointer to a valid string that will be converted (local, or wide) * @return pointer to the converted string; if failed string is of zero-length * @see the current code-page comes from video\win32_v.cpp, event-notification * WM_INPUTLANGCHANGE */ const char *FS2OTTD(const TCHAR *name) { static char utf8_buf[512]; #if defined(UNICODE) return convert_from_fs(name, utf8_buf, lengthof(utf8_buf)); #else char *s = utf8_buf; for (; *name != '\0'; name++) { wchar_t w; int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1); if (len != 1) { DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError()); continue; } if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break; s += Utf8Encode(s, w); } *s = '\0'; return utf8_buf; #endif /* UNICODE */ }
char *TranslateTTDPatchCodes(uint32 grfid, const char *str) { char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion char *d = tmp; bool unicode = false; WChar c; size_t len = Utf8Decode(&c, str); if (c == 0x00DE) { /* The thorn ('þ') indicates a unicode string to TTDPatch */ unicode = true; str += len; } for (;;) { if (unicode && Utf8EncodedCharLen(*str) != 0) { c = Utf8Consume(&str); /* 'Magic' range of control codes. */ if (GB(c, 8, 8) == 0xE0) { c = GB(c, 0, 8); } else if (c >= 0x20) { if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; d += Utf8Encode(d, c); continue; } } else { c = (byte)*str++; } if (c == 0) break; switch (c) { case 0x01: d += Utf8Encode(d, SCC_SETX); *d++ = *str++; break; case 0x0A: break; case 0x0D: *d++ = 0x0A; break; case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break; case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break; case 0x1F: d += Utf8Encode(d, SCC_SETXY); *d++ = *str++; *d++ = *str++; break; case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: case 0x80: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD + c - 0x7B); break; case 0x81: { StringID string; string = ((uint8)*str++); string |= ((uint8)*str++) << 8; d += Utf8Encode(d, SCC_STRING_ID); d += Utf8Encode(d, MapGRFStringID(grfid, string)); break; } case 0x82: case 0x83: case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_SPEED + c - 0x82); break; case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break; case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break; case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_LITRES); break; case 0x88: d += Utf8Encode(d, SCC_BLUE); break; case 0x89: d += Utf8Encode(d, SCC_SILVER); break; case 0x8A: d += Utf8Encode(d, SCC_GOLD); break; case 0x8B: d += Utf8Encode(d, SCC_RED); break; case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break; case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break; case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break; case 0x8F: d += Utf8Encode(d, SCC_GREEN); break; case 0x90: d += Utf8Encode(d, SCC_YELLOW); break; case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break; case 0x92: d += Utf8Encode(d, SCC_CREAM); break; case 0x93: d += Utf8Encode(d, SCC_BROWN); break; case 0x94: d += Utf8Encode(d, SCC_WHITE); break; case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break; case 0x96: d += Utf8Encode(d, SCC_GRAY); break; case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break; case 0x98: d += Utf8Encode(d, SCC_BLACK); break; case 0x9A: switch (*str++) { case 0: // FALL THROUGH case 1: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break; case 3: { uint16 tmp = ((uint8)*str++); tmp |= ((uint8)*str++) << 8; d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD); d += Utf8Encode(d, tmp); } break; case 4: d += Utf8Encode(d, SCC_NEWGRF_UNPRINT); d += Utf8Encode(d, *str++); break; case 6: d += Utf8Encode(d, SCC_NEWGRF_PRINT_HEX_BYTE); break; case 7: d += Utf8Encode(d, SCC_NEWGRF_PRINT_HEX_WORD); break; case 8: d += Utf8Encode(d, SCC_NEWGRF_PRINT_HEX_DWORD); break; case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_HEX_QWORD); break; default: grfmsg(1, "missing handler for extended format code"); break; } break; case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break; case 0xAA: d += Utf8Encode(d, SCC_DOWNARROW); break; case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break; case 0xAD: d += Utf8Encode(d, SCC_CROSS); break; case 0xAF: d += Utf8Encode(d, SCC_RIGHTARROW); break; case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break; case 0xB5: d += Utf8Encode(d, SCC_LORRY); break; case 0xB6: d += Utf8Encode(d, SCC_BUS); break; case 0xB7: d += Utf8Encode(d, SCC_PLANE); break; case 0xB8: d += Utf8Encode(d, SCC_SHIP); break; case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1); break; case 0xBC: d += Utf8Encode(d, SCC_SMALLUPARROW); break; case 0xBD: d += Utf8Encode(d, SCC_SMALLDOWNARROW); break; default: /* Validate any unhandled character */ if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; d += Utf8Encode(d, c); break; } } *d = '\0'; tmp = ReallocT(tmp, strlen(tmp) + 1); return tmp; }
/** * Translate TTDPatch string codes into something OpenTTD can handle (better). * @param grfid The (NewGRF) ID associated with this string * @param language_id The (NewGRF) language ID associated with this string. * @param allow_newlines Whether newlines are allowed in the string or not. * @param str The string to translate. * @param [out] olen The length of the final string. * @param byte80 The control code to use as replacement for the 0x80-value. * @return The translated string. */ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80) { char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion char *d = tmp; bool unicode = false; WChar c; size_t len = Utf8Decode(&c, str); /* Helper variable for a possible (string) mapping. */ UnmappedChoiceList *mapping = NULL; if (c == NFO_UTF8_IDENTIFIER) { unicode = true; str += len; } for (;;) { if (unicode && Utf8EncodedCharLen(*str) != 0) { c = Utf8Consume(&str); /* 'Magic' range of control codes. */ if (GB(c, 8, 8) == 0xE0) { c = GB(c, 0, 8); } else if (c >= 0x20) { if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; d += Utf8Encode(d, c); continue; } } else { c = (byte)*str++; } if (c == '\0') break; switch (c) { case 0x01: if (str[0] == '\0') goto string_end; d += Utf8Encode(d, ' '); str++; break; case 0x0A: break; case 0x0D: if (allow_newlines) { *d++ = 0x0A; } else { grfmsg(1, "Detected newline in string that does not allow one"); } break; case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break; case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break; case 0x1F: if (str[0] == '\0' || str[1] == '\0') goto string_end; d += Utf8Encode(d, ' '); str += 2; break; case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break; case 0x80: d += Utf8Encode(d, byte80); break; case 0x81: { if (str[0] == '\0' || str[1] == '\0') goto string_end; StringID string; string = ((uint8)*str++); string |= ((uint8)*str++) << 8; d += Utf8Encode(d, SCC_NEWGRF_STRINL); d += Utf8Encode(d, MapGRFStringID(grfid, string)); break; } case 0x82: case 0x83: case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break; case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break; case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break; case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break; case 0x88: d += Utf8Encode(d, SCC_BLUE); break; case 0x89: d += Utf8Encode(d, SCC_SILVER); break; case 0x8A: d += Utf8Encode(d, SCC_GOLD); break; case 0x8B: d += Utf8Encode(d, SCC_RED); break; case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break; case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break; case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break; case 0x8F: d += Utf8Encode(d, SCC_GREEN); break; case 0x90: d += Utf8Encode(d, SCC_YELLOW); break; case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break; case 0x92: d += Utf8Encode(d, SCC_CREAM); break; case 0x93: d += Utf8Encode(d, SCC_BROWN); break; case 0x94: d += Utf8Encode(d, SCC_WHITE); break; case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break; case 0x96: d += Utf8Encode(d, SCC_GRAY); break; case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break; case 0x98: d += Utf8Encode(d, SCC_BLACK); break; case 0x9A: { int code = *str++; switch (code) { case 0x00: goto string_end; case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break; /* 0x02: ignore next colour byte is not supported. It works on the final * string and as such hooks into the string drawing routine. At that * point many things already happened, such as splitting up of strings * when drawn over multiple lines or right-to-left translations, which * make the behaviour peculiar, e.g. only happening at specific width * of windows. Or we need to add another pass over the string to just * support this. As such it is not implemented in OpenTTD. */ case 0x03: { if (str[0] == '\0' || str[1] == '\0') goto string_end; uint16 tmp = ((uint8)*str++); tmp |= ((uint8)*str++) << 8; d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD); d += Utf8Encode(d, tmp); break; } case 0x04: if (str[0] == '\0') goto string_end; d += Utf8Encode(d, SCC_NEWGRF_UNPRINT); d += Utf8Encode(d, *str++); break; case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break; case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break; case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break; /* 0x09, 0x0A are TTDPatch internal use only string codes. */ case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break; case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break; case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break; case 0x0E: case 0x0F: { if (str[0] == '\0') goto string_end; const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id); int index = *str++; int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1; if (mapped >= 0) { d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE); d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1); } break; } case 0x10: case 0x11: if (str[0] == '\0') goto string_end; if (mapping == NULL) { if (code == 0x10) str++; // Skip the index grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default"); break; } else { /* Terminate the previous string. */ *d = '\0'; int index = (code == 0x10 ? *str++ : 0); if (mapping->strings.Contains(index)) { grfmsg(1, "duplicate choice list string, ignoring"); d++; } else { d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1); } } break; case 0x12: if (mapping == NULL) { grfmsg(1, "choice list end marker found when not expected"); } else { /* Terminate the previous string. */ *d = '\0'; /* Now we can start flushing everything and clean everything up. */ d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id)); delete mapping; mapping = NULL; } break; case 0x13: case 0x14: case 0x15: if (str[0] == '\0') goto string_end; if (mapping != NULL) { grfmsg(1, "choice lists can't be stacked, it's going to get messy now..."); if (code != 0x14) str++; } else { static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST }; mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++); } break; case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16); break; default: grfmsg(1, "missing handler for extended format code"); break; } break; } case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW); break; case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW); break; case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break; case 0xAD: d += Utf8Encode(d, SCC_CROSS); break; case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW); break; case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break; case 0xB5: d += Utf8Encode(d, SCC_LORRY); break; case 0xB6: d += Utf8Encode(d, SCC_BUS); break; case 0xB7: d += Utf8Encode(d, SCC_PLANE); break; case 0xB8: d += Utf8Encode(d, SCC_SHIP); break; case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1); break; case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW); break; case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break; default: /* Validate any unhandled character */ if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; d += Utf8Encode(d, c); break; } } string_end: if (mapping != NULL) { grfmsg(1, "choice list was incomplete, the whole list is ignored"); delete mapping; } *d = '\0'; if (olen != NULL) *olen = d - tmp + 1; tmp = ReallocT(tmp, d - tmp + 1); return tmp; }
/** * Flush this choice list into the old d variable. * @param lm The current language mapping. * @return The new location of the output string. */ char *Flush(const LanguageMap *lm) { if (!this->strings.Contains(0)) { /* In case of a (broken) NewGRF without a default, * assume an empty string. */ grfmsg(1, "choice list misses default value"); this->strings[0] = strdup(""); } char *d = old_d; if (lm == NULL && this->type != SCC_PLURAL_LIST) { /* In case there is no mapping, just ignore everything but the default. * A probable cause for this happening is when the language file has * been removed by the user and as such no mapping could be made. */ size_t len = strlen(this->strings[0]); memcpy(d, this->strings[0], len); return d + len; } d += Utf8Encode(d, this->type); if (this->type == SCC_SWITCH_CASE) { /* * Format for case switch: * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT> * Each LEN is printed using 2 bytes in big endian order. */ /* "<NUM CASES>" */ int count = 0; for (uint8 i = 0; i < _current_language->num_cases; i++) { /* Count the ones we have a mapped string for. */ if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++; } *d++ = count; for (uint8 i = 0; i < _current_language->num_cases; i++) { /* Resolve the string we're looking for. */ int idx = lm->GetReverseMapping(i, false); if (!this->strings.Contains(idx)) continue; char *str = this->strings[idx]; /* "<CASEn>" */ *d++ = i + 1; /* "<LENn>" */ size_t len = strlen(str) + 1; *d++ = GB(len, 8, 8); *d++ = GB(len, 0, 8); /* "<STRINGn>" */ memcpy(d, str, len); d += len; } /* "<STRINGDEFAULT>" */ size_t len = strlen(this->strings[0]) + 1; memcpy(d, this->strings[0], len); d += len; } else { if (this->type == SCC_PLURAL_LIST) { *d++ = lm->plural_form; } /* * Format for choice list: * <OFFSET> <NUM CHOICES> <LENs> <STRINGs> */ /* "<OFFSET>" */ *d++ = this->offset - 0x80; /* "<NUM CHOICES>" */ int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS); *d++ = count; /* "<LENs>" */ for (int i = 0; i < count; i++) { int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1); const char *str = this->strings[this->strings.Contains(idx) ? idx : 0]; size_t len = strlen(str) + 1; if (len > 0xFF) grfmsg(1, "choice list string is too long"); *d++ = GB(len, 0, 8); } /* "<STRINGs>" */ for (int i = 0; i < count; i++) { int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1); const char *str = this->strings[this->strings.Contains(idx) ? idx : 0]; /* Limit the length of the string we copy to 0xFE. The length is written above * as a byte and we need room for the final '\0'. */ size_t len = min<size_t>(0xFE, strlen(str)); memcpy(d, str, len); d += len; *d++ = '\0'; } } return d; }