truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" tron@1317: #include "string.h" tron@1309: #include "strings.h" tron@507: #include "table/strings.h" tron@1306: #include "namegen.h" truelight@0: #include "station.h" truelight@0: #include "town.h" truelight@0: #include "vehicle.h" darkvater@6: #include "news.h" tron@430: #include "screenshot.h" truelight@0: tron@1312: static char *StationGetSpecialString(char *buff); tron@1312: static char *GetSpecialTownNameString(char *buff, int ind); tron@1312: static char *GetSpecialPlayerNameString(char *buff, int ind); truelight@0: tron@1312: static char *DecodeString(char *buff, const char *str); truelight@0: darkvater@663: extern const char _openttd_revision[]; darkvater@659: tron@1319: typedef struct LanguagePack { truelight@0: uint32 ident; truelight@0: uint32 version; // 32-bits of auto generated version info which is basically a hash of strings.h truelight@0: char name[32]; // the international name of this language truelight@0: char own_name[32]; // the localized name of this language truelight@0: uint16 offsets[32]; // the offsets tron@1319: char data[VARARRAY_SIZE]; tron@1319: } LanguagePack; tron@1319: tron@1319: static char **_langpack_offs; tron@1319: static LanguagePack *_langpack; tron@1319: static uint _langtab_num[32]; // Offset into langpack offs tron@1319: static uint _langtab_start[32]; // Offset into langpack offs truelight@0: tron@1321: const StringID _currency_string_list[] = { dominik@762: STR_CURR_GBP, dominik@762: STR_CURR_USD, dominik@762: STR_CURR_EUR, truelight@0: STR_CURR_YEN, truelight@0: STR_CURR_ATS, truelight@0: STR_CURR_BEF, dominik@762: STR_CURR_CHF, dominik@762: STR_CURR_CZK, dominik@762: STR_CURR_DEM, truelight@0: STR_CURR_DKK, dominik@762: STR_CURR_ESP, truelight@0: STR_CURR_FIM, dominik@762: STR_CURR_FRF, truelight@0: STR_CURR_GRD, dominik@762: STR_CURR_HUF, dominik@762: STR_CURR_ISK, truelight@0: STR_CURR_ITL, dominik@762: STR_CURR_NLG, dominik@762: STR_CURR_NOK, dominik@762: STR_CURR_PLN, dominik@762: STR_CURR_ROL, truelight@0: STR_CURR_RUR, dominik@762: STR_CURR_SEK, dominik@759: STR_CURR_CUSTOM, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: tron@1321: static const StringID _cargo_string_list[NUM_LANDSCAPE][NUM_CARGO] = { tron@1316: { /* LT_NORMAL */ tron@1316: STR_PASSENGERS, tron@1316: STR_TONS, tron@1316: STR_BAGS, tron@1316: STR_LITERS, tron@1316: STR_ITEMS, tron@1316: STR_CRATES, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_BAGS, tron@1316: STR_RES_OTHER tron@1316: }, tron@1316: tron@1316: { /* LT_HILLY */ tron@1316: STR_PASSENGERS, tron@1316: STR_TONS, tron@1316: STR_BAGS, tron@1316: STR_LITERS, tron@1316: STR_ITEMS, tron@1316: STR_CRATES, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_RES_OTHER, tron@1316: STR_TONS, tron@1316: STR_BAGS, tron@1316: STR_TONS tron@1316: }, tron@1316: tron@1316: { /* LT_DESERT */ tron@1316: STR_PASSENGERS, tron@1316: STR_LITERS, tron@1316: STR_BAGS, tron@1316: STR_LITERS, tron@1316: STR_TONS, tron@1316: STR_CRATES, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_LITERS, tron@1316: STR_BAGS, tron@1316: STR_TONS tron@1316: }, tron@1316: tron@1316: { /* LT_CANDY */ tron@1316: STR_PASSENGERS, tron@1316: STR_TONS, tron@1316: STR_BAGS, tron@1316: STR_NOTHING, tron@1316: STR_NOTHING, tron@1316: STR_TONS, tron@1316: STR_TONS, tron@1316: STR_LITERS, tron@1316: STR_TONS, tron@1316: STR_NOTHING, tron@1316: STR_LITERS, tron@1316: STR_NOTHING tron@1316: } truelight@0: }; truelight@0: tron@1312: static char *str_cat(char *dst, const char *src) truelight@0: { tron@1316: while ((*dst++ = *src++) != '\0') {} truelight@0: return dst - 1; truelight@0: } truelight@0: tron@1321: static const char *GetStringPtr(StringID string) truelight@0: { truelight@0: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)]; truelight@0: } truelight@0: tron@1321: char *GetString(char *buffr, StringID string) truelight@0: { truelight@0: uint index = string & 0x7FF; truelight@0: uint tab = string >> 11; truelight@0: tron@1321: switch (string) { tron@1321: case 0: tron@1321: error("!invalid string id 0 in GetString"); tron@1321: break; truelight@0: tron@1321: case 0x30D1: tron@1321: return StationGetSpecialString(buffr); truelight@0: tron@1321: case STR_SPEC_SCREENSHOT_NAME: tron@1321: return DecodeString(buffr, _screenshot_name); tron@1321: } truelight@0: tron@1321: switch (tab) { tron@1321: case 4: tron@1321: if (index >= 0xC0) return GetSpecialTownNameString(buffr, index - 0xC0); tron@1321: break; tron@1321: tron@1321: case 14: tron@1321: if (index >= 0xE4) return GetSpecialPlayerNameString(buffr, index - 0xE4); tron@1321: break; tron@1321: tron@1321: case 15: tron@1321: return GetName(index, buffr); tron@1321: tron@1321: case 31: // special or dynamic strings tron@1321: return DecodeString(buffr, _userstring); tron@1321: tron@1321: default: tron@1321: break; truelight@0: } truelight@0: truelight@0: if (index >= _langtab_num[tab]) tron@1321: error( tron@1321: "!String 0x%X is invalid. " tron@1321: "Probably because an old version of the .lng file.\n", string tron@1321: ); truelight@0: truelight@0: return DecodeString(buffr, GetStringPtr(string)); truelight@0: } truelight@0: tron@1309: void InjectDParam(int amount) truelight@0: { truelight@193: memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint32)); truelight@0: } truelight@0: truelight@0: tron@1093: int32 GetParamInt32(void) truelight@0: { tron@534: int32 result = GetDParam(0); tron@425: memmove(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: tron@1093: static int64 GetParamInt64(void) truelight@0: { tron@534: int64 result = GetDParam(0) + ((uint64)GetDParam(1) << 32); tron@425: memmove(&_decode_parameters[0], &_decode_parameters[2], sizeof(uint32) * (lengthof(_decode_parameters)-2)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: tron@1093: int GetParamInt16(void) truelight@0: { tron@534: int result = (int16)GetDParam(0); tron@425: memmove(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: tron@1093: int GetParamInt8(void) truelight@0: { tron@534: int result = (int8)GetDParam(0); tron@425: memmove(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: tron@1093: int GetParamUint16(void) truelight@0: { tron@534: int result = GetDParam(0); tron@425: memmove(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: static const uint32 _divisor_table[] = { truelight@0: 1000000000, truelight@0: 100000000, truelight@0: 10000000, truelight@0: 1000000, truelight@0: truelight@0: 100000, truelight@0: 10000, truelight@0: 1000, truelight@0: 100, truelight@0: 10, truelight@0: 1 truelight@0: }; truelight@0: tron@1312: static char *FormatCommaNumber(char *buff, int32 number) truelight@0: { truelight@0: uint32 quot,divisor; truelight@0: int i; truelight@0: uint32 tot; truelight@0: uint32 num; truelight@0: truelight@0: if (number < 0) { truelight@0: *buff++ = '-'; truelight@0: number = -number; truelight@0: } truelight@0: truelight@0: num = number; truelight@0: truelight@0: tot = 0; tron@1316: for (i = 0; i != 10; i++) { truelight@0: divisor = _divisor_table[i]; truelight@0: quot = 0; truelight@0: if (num >= divisor) { truelight@0: quot = num / _divisor_table[i]; truelight@0: num = num % _divisor_table[i]; truelight@0: } tron@1316: if (tot |= quot || i == 9) { tron@1312: *buff++ = '0' + quot; tron@1316: if (i == 0 || i == 3 || i == 6) *buff++ = ','; truelight@0: } truelight@0: } truelight@0: tron@1316: *buff = '\0'; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: tron@1312: static char *FormatNoCommaNumber(char *buff, int32 number) truelight@0: { truelight@0: uint32 quot,divisor; truelight@0: int i; truelight@0: uint32 tot; truelight@0: uint32 num; truelight@0: truelight@0: if (number < 0) { truelight@0: *buff++ = '-'; truelight@0: number = -number; truelight@0: } truelight@0: truelight@0: num = number; truelight@0: truelight@0: tot = 0; tron@1316: for (i = 0; i != 10; i++) { truelight@0: divisor = _divisor_table[i]; truelight@0: quot = 0; truelight@0: if (num >= divisor) { truelight@0: quot = num / _divisor_table[i]; truelight@0: num = num % _divisor_table[i]; truelight@0: } tron@1316: if (tot |= quot || i == 9) { tron@1312: *buff++ = '0' + quot; truelight@0: } truelight@0: } truelight@0: tron@1316: *buff = '\0'; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: truelight@0: tron@1312: static char *FormatYmdString(char *buff, uint16 number) truelight@0: { tron@1312: const char *src; truelight@0: YearMonthDay ymd; truelight@0: truelight@0: ConvertDayToYMD(&ymd, number); truelight@0: tron@1316: for (src = GetStringPtr(ymd.day + STR_01AC_1ST - 1); (*buff++ = *src++) != '\0';) {} truelight@193: truelight@0: buff[-1] = ' '; truelight@0: memcpy(buff, GetStringPtr(STR_0162_JAN + ymd.month), 4); truelight@0: buff[3] = ' '; truelight@0: darkvater@970: return FormatNoCommaNumber(buff+4, ymd.year + MAX_YEAR_BEGIN_REAL); truelight@0: } truelight@0: tron@1312: static char *FormatMonthAndYear(char *buff, uint16 number) truelight@0: { truelight@0: const char *src; truelight@0: YearMonthDay ymd; truelight@0: truelight@0: ConvertDayToYMD(&ymd, number); truelight@0: tron@1316: for (src = GetStringPtr(STR_MONTH_JAN + ymd.month); (*buff++ = *src++) != '\0';) {} truelight@0: buff[-1] = ' '; truelight@0: darkvater@970: return FormatNoCommaNumber(buff, ymd.year + MAX_YEAR_BEGIN_REAL); truelight@0: } truelight@0: tron@1312: static char *FormatTinyDate(char *buff, uint16 number) dominik@1097: { dominik@1097: YearMonthDay ymd; dominik@1097: dominik@1097: ConvertDayToYMD(&ymd, number); dominik@1097: buff += sprintf(buff, " %02i-%02i-%04i", ymd.day, ymd.month + 1, ymd.year + MAX_YEAR_BEGIN_REAL); dominik@1097: dominik@1097: return buff; dominik@1097: } dominik@1097: tron@1093: uint GetCurrentCurrencyRate(void) tron@1093: { tron@1316: return _currency_specs[_opt.currency].rate; truelight@26: } truelight@26: tron@1312: static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, int64 number, bool compact) truelight@0: { truelight@0: const char *s; truelight@0: char c; truelight@0: char buf[40], *p; truelight@0: int j; truelight@0: truelight@0: // multiply by exchange rate truelight@0: number *= spec->rate; truelight@0: truelight@0: // convert from negative tron@1316: if (number < 0) { tron@1316: *buff++ = '-'; tron@1316: number = -number; tron@1316: } truelight@193: dominik@788: // add prefix part dominik@788: s = spec->prefix; tron@1316: while (s != spec->prefix + lengthof(spec->prefix) && (c = *s++) != '\0') *buff++ = c; truelight@0: truelight@0: // for huge numbers, compact the number into k or M truelight@0: if (compact) { truelight@0: compact = 0; truelight@0: if (number >= 1000000000) { truelight@0: number = (number + 500000) / 1000000; truelight@0: compact = 'M'; truelight@0: } else if (number >= 1000000) { truelight@0: number = (number + 500) / 1000; truelight@0: compact = 'k'; truelight@193: } truelight@0: } truelight@193: truelight@0: // convert to ascii number and add commas truelight@0: p = buf; truelight@0: j = 4; truelight@193: do { tron@1316: if (--j == 0) { tron@1316: *p++ = spec->separator; tron@1316: j = 3; tron@1316: } truelight@0: *p++ = '0' + number % 10; truelight@0: } while (number /= 10); truelight@0: do *buff++ = *--p; while (p != buf); truelight@0: truelight@0: if (compact) *buff++ = compact; truelight@0: dominik@788: // add suffix part dominik@788: s = spec->suffix; tron@1316: while (s != spec->suffix + lengthof(spec->suffix) && (c = *s++) != '\0') *buff++ = c; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: tron@1312: static char *DecodeString(char *buff, const char *str) truelight@0: { truelight@0: byte b; truelight@0: tron@1316: while ((b = *str++) != '\0') { tron@1316: switch (b) { truelight@0: case 0x1: // {SETX} truelight@0: *buff++ = b; truelight@0: *buff++ = *str++; truelight@0: break; truelight@0: case 0x2: // {SETXY} truelight@0: *buff++ = b; truelight@0: *buff++ = *str++; truelight@0: *buff++ = *str++; truelight@0: break; truelight@0: case 0x7B: // {COMMA32} truelight@0: buff = FormatCommaNumber(buff, GetParamInt32()); truelight@0: break; truelight@0: case 0x7C: // {COMMA16} truelight@0: buff = FormatCommaNumber(buff, GetParamInt16()); truelight@0: break; truelight@0: case 0x7D: // {COMMA8} truelight@0: buff = FormatCommaNumber(buff, GetParamInt8()); truelight@0: break; truelight@0: case 0x7E: // {NUMU16} truelight@0: buff = FormatNoCommaNumber(buff, GetParamInt16()); truelight@0: break; truelight@0: case 0x7F: // {CURRENCY} truelight@0: buff = FormatGenericCurrency(buff, &_currency_specs[_opt.currency], GetParamInt32(), false); truelight@0: break; truelight@0: // 0x80 is reserved for EURO truelight@0: case 0x81: // {STRINL} truelight@0: str += 2; truelight@0: buff = GetString(buff, READ_LE_UINT16(str-2)); truelight@0: break; truelight@0: case 0x82: // {DATE_LONG} truelight@0: buff = FormatYmdString(buff, GetParamUint16()); truelight@0: break; truelight@0: case 0x83: // {DATE_SHORT} truelight@0: buff = FormatMonthAndYear(buff, GetParamUint16()); truelight@0: break; truelight@0: case 0x84: {// {VELOCITY} truelight@0: int value = GetParamInt16(); truelight@0: if (_opt.kilometers) value = value * 1648 >> 10; truelight@0: buff = FormatCommaNumber(buff, value); truelight@0: if (_opt.kilometers) { truelight@0: memcpy(buff, " km/h", 5); truelight@0: buff += 5; truelight@0: } else { truelight@0: memcpy(buff, " mph", 4); truelight@0: buff += 4; truelight@0: } truelight@0: break; truelight@0: } truelight@193: truelight@193: // 0x85 is used as escape character.. truelight@0: case 0x85: tron@1316: switch (*str++) { darkvater@236: case 0: /* {CURRCOMPACT} */ truelight@0: buff = FormatGenericCurrency(buff, &_currency_specs[_opt.currency], GetParamInt32(), true); truelight@0: break; darkvater@236: case 1: /* {INT32} */ truelight@0: buff = FormatNoCommaNumber(buff, GetParamInt32()); truelight@0: break; darkvater@236: case 2: /* {REV} */ tron@1312: buff = str_cat(buff, _openttd_revision); truelight@0: break; darkvater@236: case 3: { /* {SHORTCARGO} */ darkvater@236: // Short description of cargotypes. Layout: truelight@0: // 8-bit = cargo type truelight@0: // 16-bit = cargo count tron@1318: const char *s; tron@1321: StringID cargo_str = _cargo_string_list[_opt.landscape][(byte)GetParamInt8()]; truelight@0: uint16 multiplier = (cargo_str == STR_LITERS) ? 1000 : 1; truelight@0: // liquid type of cargo is multiplied by 100 to get correct amount tron@1316: buff = FormatCommaNumber(buff, GetParamInt16() * multiplier); truelight@0: s = GetStringPtr(cargo_str); truelight@193: truelight@0: memcpy(buff++, " ", 1); truelight@0: while (*s) *buff++ = *s++; tron@1316: } break; darkvater@236: case 4: /* {CURRCOMPACT64} */ darkvater@236: // 64 bit compact currency-unit darkvater@236: buff = FormatGenericCurrency(buff, &_currency_specs[_opt.currency], GetParamInt64(), true); truelight@0: break; truelight@0: truelight@0: default: truelight@0: error("!invalid escape sequence in string"); truelight@0: } truelight@0: break; truelight@0: truelight@0: case 0x86: // {SKIP} truelight@0: GetParamInt16(); truelight@0: //assert(0); truelight@0: break; truelight@0: case 0x87: { // {VOLUME} tron@1318: const char *s; truelight@0: buff = FormatCommaNumber(buff, GetParamInt16() * 1000); truelight@0: memcpy(buff++, " ", 1); truelight@0: s = GetStringPtr(STR_LITERS); truelight@0: while (*s) *buff++ = *s++; truelight@0: break; truelight@0: } truelight@193: truelight@0: case 0x88: // {STRING} truelight@0: buff = GetString(buff, (uint16)GetParamUint16()); truelight@0: break; truelight@0: truelight@0: case 0x99: { // {CARGO} truelight@0: // Layout now is: truelight@0: // 8bit - cargo type truelight@0: // 16-bit - cargo count truelight@0: int cargo_str = _cargoc.names_long_s[GetParamInt8()]; truelight@0: // Now check if the cargo count is 1, if it is, increase string by 32. tron@534: if (GetDParam(0) != 1) cargo_str += 32; truelight@0: buff = GetString(buff, cargo_str); truelight@0: break; truelight@0: } truelight@0: truelight@0: case 0x9A: { // {STATION} truelight@0: Station *st; tron@1309: InjectDParam(1); truelight@919: st = GetStation(GetDParam(1)); tron@1316: if (st->xy == 0) { // station doesn't exist anymore darkvater@64: buff = GetString(buff, STR_UNKNOWN_DESTINATION); darkvater@64: break; darkvater@64: } tron@534: SetDParam(0, st->town->townnametype); tron@534: SetDParam(1, st->town->townnameparts); truelight@0: buff = GetString(buff, st->string_id); truelight@0: break; truelight@0: } truelight@0: case 0x9B: { // {TOWN} truelight@0: Town *t; truelight@919: t = GetTown(GetDParam(0)); truelight@0: assert(t->xy); tron@534: SetDParam(0, t->townnameparts); truelight@0: buff = GetString(buff, t->townnametype); truelight@0: break; truelight@0: } truelight@0: truelight@0: case 0x9C: { // {CURRENCY64} truelight@193: buff = FormatGenericCurrency(buff, &_currency_specs[_opt.currency], GetParamInt64(), false); truelight@0: break; truelight@0: } truelight@0: darkvater@395: case 0x9D: { // {WAYPOINT} tron@534: Waypoint *cp = &_waypoints[GetDParam(0)]; truelight@0: StringID str; truelight@0: int idx; truelight@0: if (~cp->town_or_string & 0xC000) { truelight@0: GetParamInt32(); // skip it truelight@0: str = cp->town_or_string; truelight@0: } else { truelight@0: idx = (cp->town_or_string >> 8) & 0x3F; truelight@0: if (idx == 0) { darkvater@395: str = STR_WAYPOINTNAME_CITY; truelight@0: } else { tron@1309: InjectDParam(1); tron@534: SetDParam(1, idx + 1); darkvater@395: str = STR_WAYPOINTNAME_CITY_SERIAL; truelight@0: } tron@534: SetDParam(0, cp->town_or_string & 0xFF); truelight@0: } truelight@0: truelight@0: buff = GetString(buff, str); truelight@0: } break; dominik@1097: dominik@1097: case 0x9E: { // {DATE_TINY} dominik@1097: buff = FormatTinyDate(buff, GetParamUint16()); dominik@1097: break; dominik@1097: } dominik@1097: truelight@0: // case 0x88..0x98: // {COLORS} truelight@0: // case 0xE: // {TINYFONT} truelight@0: // case 0xF: // {BIGFONT} truelight@0: // 0x9E is the highest number that is available. truelight@0: default: truelight@0: *buff++ = b; truelight@0: } truelight@0: } tron@1316: *buff = '\0'; truelight@0: return buff; truelight@0: } truelight@0: truelight@0: tron@1312: static char *StationGetSpecialString(char *buff) truelight@0: { truelight@0: int x = GetParamInt8(); tron@1316: if (x & 0x01) *buff++ = '\xB4'; tron@1316: if (x & 0x02) *buff++ = '\xB5'; tron@1316: if (x & 0x04) *buff++ = '\xB6'; tron@1316: if (x & 0x08) *buff++ = '\xB7'; tron@1316: if (x & 0x10) *buff++ = '\xB8'; tron@1316: *buff = '\0'; truelight@0: return buff; truelight@0: } truelight@0: tron@1316: static char *GetSpecialTownNameString(char *buff, int ind) tron@1316: { truelight@0: uint32 x = GetParamInt32(); truelight@193: truelight@0: _town_name_generators[ind](buff, x); truelight@0: tron@1316: while (*buff != '\0') buff++; truelight@0: return buff; truelight@0: } truelight@0: truelight@0: static const char * const _silly_company_names[] = { truelight@0: "Bloggs Brothers", truelight@0: "Tiny Transport Ltd.", truelight@0: "Express Travel", truelight@0: "Comfy-Coach & Co.", truelight@0: "Crush & Bump Ltd.", truelight@0: "Broken & Late Ltd.", truelight@0: "Sam Speedy & Son", truelight@0: "Supersonic Travel", truelight@0: "Mike's Motors", truelight@0: "Lightning International", truelight@0: "Pannik & Loozit Ltd.", truelight@0: "Inter-City Transport", truelight@0: "Getout & Pushit Ltd.", truelight@0: }; truelight@0: truelight@0: static const char * const _surname_list[] = { truelight@0: "Adams", truelight@0: "Allan", truelight@0: "Baker", truelight@0: "Bigwig", truelight@0: "Black", truelight@0: "Bloggs", truelight@0: "Brown", truelight@0: "Campbell", truelight@0: "Gordon", truelight@0: "Hamilton", truelight@0: "Hawthorn", truelight@0: "Higgins", truelight@0: "Green", truelight@0: "Gribble", truelight@0: "Jones", truelight@0: "McAlpine", truelight@0: "MacDonald", truelight@0: "McIntosh", truelight@0: "Muir", truelight@0: "Murphy", truelight@0: "Nelson", truelight@0: "O'Donnell", truelight@0: "Parker", truelight@0: "Phillips", truelight@0: "Pilkington", truelight@0: "Quigley", truelight@0: "Sharkey", truelight@0: "Thomson", truelight@0: "Watkins", truelight@0: "Grumpy", truelight@0: "Dozy", truelight@0: "Speedy", truelight@0: "Nosey", truelight@0: "Dribble", truelight@0: "Mushroom", truelight@0: "Cabbage", truelight@0: "Sniffle", truelight@0: "Fishy", truelight@0: "Swindle", truelight@0: "Sneaky", truelight@0: "Nutkins", truelight@0: }; truelight@0: tron@1312: static const char _initial_name_letters[] = { tron@1321: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', tron@1321: 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', truelight@0: }; truelight@0: tron@1312: static char *GenAndCoName(char *buff) truelight@0: { truelight@0: uint32 x = GetParamInt32(); truelight@0: uint base,num; truelight@0: truelight@0: base = 0; truelight@0: num = 29; truelight@0: if (_opt.landscape == LT_CANDY) { truelight@0: base = num; truelight@0: num = 12; truelight@0: } truelight@0: truelight@0: buff = str_cat(buff, _surname_list[base + ((num * (byte)(x >> 16)) >> 8)]); truelight@0: buff = str_cat(buff, " & Co."); truelight@0: truelight@0: return buff; truelight@0: } truelight@0: tron@1312: static char *GenPlayerName_4(char *buff) truelight@0: { truelight@0: uint32 x = GetParamInt32(); truelight@0: uint i, base, num; truelight@0: tron@1316: buff[0] = _initial_name_letters[(sizeof(_initial_name_letters) * (byte)x) >> 8]; truelight@0: buff[1] = '.'; darkvater@233: buff[2] = ' '; // Insert a space after initial and period "I. Firstname" instead of "I.Firstname" darkvater@233: buff += 3; truelight@0: truelight@0: i = ((sizeof(_initial_name_letters) + 35) * (byte)(x >> 8)) >> 8; truelight@0: if (i < sizeof(_initial_name_letters)) { truelight@0: buff[0] = _initial_name_letters[i]; truelight@0: buff[1] = '.'; darkvater@233: buff[2] = ' '; // Insert a space after initial and period "I. J. Firstname" instead of "I.J.Firstname" darkvater@233: buff += 3; truelight@0: } truelight@0: truelight@0: base = 0; truelight@0: num = 29; truelight@0: if (_opt.landscape == LT_CANDY) { truelight@0: base = num; truelight@0: num = 12; truelight@0: } truelight@0: truelight@0: buff = str_cat(buff, _surname_list[base + ((num * (byte)(x >> 16)) >> 8)]); truelight@0: truelight@0: return buff; truelight@0: } truelight@0: truelight@0: static const char * const _song_names[] = { truelight@0: "Tycoon DELUXE Theme", truelight@0: "Easy Driver", truelight@0: "Little Red Diesel", truelight@0: "Cruise Control", truelight@0: "Don't Walk!", truelight@0: "Fell Apart On Me", truelight@0: "City Groove", truelight@0: "Funk Central", truelight@0: "Stoke It", truelight@0: "Road Hog", truelight@0: "Aliens Ate My Railway", truelight@0: "Snarl Up", truelight@0: "Stroll On", truelight@0: "Can't Get There From Here", truelight@0: "Sawyer's Tune", truelight@0: "Hold That Train!", truelight@0: "Movin' On", truelight@0: "Goss Groove", truelight@0: "Small Town", truelight@0: "Broomer's Oil Rag", truelight@0: "Jammit", truelight@0: "Hard Drivin'" truelight@0: }; truelight@0: tron@1312: static char *GetSpecialPlayerNameString(char *buff, int ind) truelight@0: { tron@1316: switch (ind) { tron@1321: case 1: // not used tron@1321: return str_cat(buff, _silly_company_names[GetParamInt32() & 0xFFFF]); truelight@0: tron@1321: case 2: // used for Foobar & Co company names tron@1321: return GenAndCoName(buff); truelight@0: tron@1321: case 3: // President name tron@1321: return GenPlayerName_4(buff); truelight@0: tron@1321: case 4: // song names tron@1321: return str_cat(buff, _song_names[GetParamUint16() - 1]); truelight@0: } truelight@0: truelight@0: // town name? tron@1316: if (IS_INT_INSIDE(ind - 6, 0, SPECSTR_TOWNNAME_LAST-SPECSTR_TOWNNAME_START + 1)) { truelight@0: buff = GetSpecialTownNameString(buff, ind - 6); truelight@0: return str_cat(buff, " Transport"); truelight@0: } truelight@0: truelight@0: // language name? truelight@0: if (IS_INT_INSIDE(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) { truelight@0: int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4); tron@1319: return str_cat(buff, i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name); truelight@0: } truelight@0: truelight@0: // resolution size? truelight@0: if (IS_INT_INSIDE(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) { truelight@0: int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4); truelight@0: return buff + sprintf(buff, "%dx%d", _resolutions[i][0], _resolutions[i][1]); truelight@0: } truelight@0: truelight@0: // screenshot format name? truelight@0: if (IS_INT_INSIDE(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) { truelight@0: int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4); truelight@0: return str_cat(buff, GetScreenshotFormatDesc(i)); truelight@0: } truelight@0: truelight@0: assert(0); truelight@0: return NULL; truelight@0: } truelight@0: truelight@0: // remap a string ID from the old format to the new format truelight@0: StringID RemapOldStringID(StringID s) truelight@0: { tron@1321: switch (s) { tron@1321: case 0x0006: return STR_SV_EMPTY; tron@1321: case 0x7000: return STR_SV_UNNAMED; tron@1321: case 0x70E4: return SPECSTR_PLAYERNAME_ENGLISH; tron@1321: case 0x70E9: return SPECSTR_PLAYERNAME_ENGLISH; tron@1321: case 0x8864: return STR_SV_TRAIN_NAME; tron@1321: case 0x902B: return STR_SV_ROADVEH_NAME; tron@1321: case 0x9830: return STR_SV_SHIP_NAME; tron@1321: case 0xA02F: return STR_SV_AIRCRAFT_NAME; tron@1321: tron@1321: default: tron@1321: if (IS_INT_INSIDE(s, 0x300F, 0x3030)) tron@1321: return s - 0x300F + STR_SV_STNAME; tron@1321: else tron@1321: return s; tron@1321: } truelight@0: } truelight@0: tron@1316: bool ReadLanguagePack(int lang_index) tron@1316: { truelight@0: int tot_count, i; tron@1319: LanguagePack *lang_pack; truelight@0: size_t len; tron@1312: char **langpack_offs; tron@1312: char *s; truelight@0: truelight@0: { truelight@0: char *lang = str_fmt("%s%s", _path.lang_dir, _dynlang.ent[lang_index].file); truelight@0: lang_pack = ReadFileToMem(lang, &len, 100000); truelight@0: free(lang); truelight@0: } truelight@0: if (lang_pack == NULL) return false; tron@1319: if (len < sizeof(LanguagePack) || tron@1319: lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) || tron@1319: lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) { truelight@0: free(lang_pack); truelight@0: return false; truelight@0: } truelight@193: truelight@0: #if defined(TTD_BIG_ENDIAN) tron@1316: for (i = 0; i != 32; i++) { tron@1319: lang_pack->offsets[i] = READ_LE_UINT16(&lang_pack->offsets[i]); truelight@0: } truelight@0: #endif truelight@0: truelight@0: tot_count = 0; tron@1316: for (i = 0; i != 32; i++) { tron@1319: uint num = lang_pack->offsets[i]; truelight@0: _langtab_start[i] = tot_count; truelight@0: _langtab_num[i] = num; truelight@0: tot_count += num; truelight@0: } truelight@0: truelight@0: // Allocate offsets tron@1312: langpack_offs = malloc(tot_count * sizeof(*langpack_offs)); truelight@0: truelight@0: // Fill offsets tron@1319: s = lang_pack->data; tron@1316: for (i = 0; i != tot_count; i++) { tron@1312: len = (byte)*s; tron@1316: *s++ = '\0'; // zero terminate the string before. tron@1316: if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++; truelight@0: langpack_offs[i] = s; truelight@0: s += len; truelight@0: } truelight@0: tron@1321: free(_langpack); truelight@0: _langpack = lang_pack; truelight@0: tron@1321: free(_langpack_offs); truelight@0: _langpack_offs = langpack_offs; truelight@0: truelight@0: ttd_strlcpy(_dynlang.curr_file, _dynlang.ent[lang_index].file, sizeof(_dynlang.curr_file)); truelight@0: truelight@0: truelight@0: _dynlang.curr = lang_index; truelight@0: return true; truelight@0: } truelight@0: truelight@0: // make a list of the available language packs. put the data in _dynlang struct. tron@1093: void InitializeLanguagePacks(void) truelight@0: { truelight@0: DynamicLanguages *dl = &_dynlang; tron@1321: int i; tron@1321: int n; tron@1321: int m; tron@1321: int def; tron@1319: LanguagePack hdr; truelight@0: FILE *in; truelight@0: char *files[32]; truelight@0: truelight@0: n = GetLanguageList(files, lengthof(files)); truelight@0: truelight@0: def = 0; // default language file truelight@0: truelight@0: // go through the language files and make sure that they are valid. tron@1316: for (i = m = 0; i != n; i++) { tron@1321: int j; tron@1321: truelight@0: char *s = str_fmt("%s%s", _path.lang_dir, files[i]); truelight@0: in = fopen(s, "rb"); truelight@0: free(s); tron@1316: if (in == NULL || truelight@0: (j = fread(&hdr, sizeof(hdr), 1, in), fclose(in), j) != 1 || truelight@0: hdr.ident != TO_LE32(LANGUAGE_PACK_IDENT) || truelight@0: hdr.version != TO_LE32(LANGUAGE_PACK_VERSION)) { truelight@0: free(files[i]); truelight@0: continue; truelight@0: } truelight@0: truelight@0: dl->ent[m].file = files[i]; truelight@0: dl->ent[m].name = strdup(hdr.name); truelight@193: tron@1316: if (strcmp(hdr.name, "English") == 0) def = m; truelight@0: truelight@0: m++; truelight@0: } truelight@0: truelight@0: if (m == 0) truelight@0: error(n == 0 ? "No available language packs" : "Invalid version of language packs"); truelight@193: truelight@0: dl->num = m; tron@1316: for (i = 0; i != dl->num; i++) truelight@0: dl->dropdown[i] = SPECSTR_LANGUAGE_START + i; truelight@0: dl->dropdown[i] = INVALID_STRING_ID; truelight@0: tron@1316: for (i = 0; i != dl->num; i++) tron@1316: if (strcmp(dl->ent[i].file, dl->curr_file) == 0) { truelight@0: def = i; truelight@0: break; truelight@0: } truelight@0: truelight@0: if (!ReadLanguagePack(def)) truelight@0: error("can't read language pack '%s'", dl->ent[def].file); truelight@0: }