truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" truelight@0: #include "station.h" truelight@0: #include "town.h" truelight@0: #include "vehicle.h" darkvater@6: #include "news.h" truelight@0: truelight@0: #define USE_TABLE(x) { assert(index < lengthof(x)); str = x[index]; break; } truelight@0: truelight@0: static byte *StationGetSpecialString(byte *buff); truelight@0: static byte *GetSpecialTownNameString(byte *buff, int ind); truelight@0: static byte *GetSpecialPlayerNameString(byte *buff, int ind); truelight@0: truelight@0: static byte *DecodeString(byte *buff, const byte *str); truelight@0: truelight@0: static byte **_langpack_offs; truelight@0: static byte *_langpack; truelight@0: static uint _langtab_num[32]; // Offset into langpack offs truelight@0: static uint _langtab_start[32]; // Offset into langpack offs truelight@0: truelight@0: #ifdef WITH_REV_HACK darkvater@287: #define WITH_REV darkvater@287: const char _openttd_revision[] = WITH_REV_HACK; darkvater@287: #else darkvater@287: #ifdef WITH_REV darkvater@287: extern const char _openttd_revision[]; darkvater@287: #endif truelight@0: #endif truelight@0: truelight@0: typedef byte *PlayerNameGeneratorProc(byte *buffr); truelight@0: truelight@0: typedef struct { 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 truelight@0: } LanguagePackHeader; truelight@0: truelight@0: typedef struct { truelight@0: uint16 rate; truelight@0: char separator; truelight@0: byte flags; truelight@0: char pre[4]; truelight@0: char post[4]; truelight@0: } CurrencySpec; truelight@0: truelight@0: enum { truelight@0: CF_TOEURO_2002 = 1, truelight@0: CF_ISEURO = 2, truelight@0: }; truelight@0: truelight@0: static const CurrencySpec _currency_specs[] = { truelight@0: { 1, ',', 0, "\xA3", "" }, // british pounds truelight@0: { 2, ',', 0, "$", "" }, // us dollars truelight@0: { 10, '.', CF_TOEURO_2002, "FF ", "" }, // french francs truelight@0: { 4, '.', CF_TOEURO_2002, "DM ", "" }, // deutsche mark truelight@0: { 200, ',', 0, "\xA5", "" }, // yen truelight@0: { 200, '.', CF_TOEURO_2002, "Pt", "" }, // spanish pesetas truelight@0: { 376, ',', CF_TOEURO_2002, "", " Ft" }, truelight@0: { 6, ' ', 0, "", " zl" }, truelight@0: { 19, ',', CF_TOEURO_2002, "ATS ", "" }, truelight@0: { 57, ',', CF_TOEURO_2002, "BEF ", "" }, truelight@0: { 10, '.', 0, "", " kr" }, truelight@0: { 8, ',', CF_TOEURO_2002, "FIM ", "" }, truelight@0: { 480, ',', CF_TOEURO_2002, "GRD ", "" }, truelight@0: { 2, ',', 0, "CHF ", "" }, truelight@0: { 3, ',', CF_TOEURO_2002, "NLG ", "" }, truelight@0: { 2730,',', CF_TOEURO_2002, "ITL ", "" }, truelight@0: { 13, '.', 0, "", " kr" }, truelight@193: { 5, ' ', 0, "", " rur" }, truelight@0: { 50, ',', 0, "", " Kc" }, truelight@0: { 130, '.', 0, "", " kr" }, truelight@0: { 11, '.', 0, "", " kr" }, darkvater@261: { 2, ',', CF_ISEURO, "€", "" }, darkvater@240: { 6, '.', 0, "", " Lei" }, truelight@0: }; truelight@0: truelight@0: const uint16 _currency_string_list[] = { truelight@0: STR_CURR_POUNDS, truelight@0: STR_CURR_DOLLARS, truelight@0: STR_CURR_FF, truelight@0: STR_CURR_DM, truelight@0: STR_CURR_YEN, truelight@0: STR_CURR_PT, truelight@0: STR_CURR_FT, truelight@0: STR_CURR_ZL, truelight@0: STR_CURR_ATS, truelight@0: STR_CURR_BEF, truelight@0: STR_CURR_DKK, truelight@0: STR_CURR_FIM, truelight@0: STR_CURR_GRD, truelight@0: STR_CURR_CHF, truelight@0: STR_CURR_NLG, truelight@0: STR_CURR_ITL, truelight@0: STR_CURR_SEK, truelight@0: STR_CURR_RUR, truelight@0: STR_CURR_CZK, truelight@0: STR_CURR_ISK, truelight@0: STR_CURR_NOK, darkvater@261: STR_CURR_EUR, darkvater@233: STR_CURR_ROL, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: truelight@0: static const uint16 _cargo_string_list[NUM_LANDSCAPE][NUM_CARGO] = { truelight@0: /* LT_NORMAL */ {STR_PASSENGERS, STR_TONS, STR_BAGS, STR_LITERS, STR_ITEMS, STR_CRATES, STR_TONS, STR_TONS, STR_TONS, STR_TONS, STR_BAGS, STR_RES_OTHER}, truelight@0: /* LT_HILLY */ {STR_PASSENGERS, STR_TONS, STR_BAGS, STR_LITERS, STR_ITEMS, STR_CRATES, STR_TONS, STR_TONS, STR_RES_OTHER, STR_TONS, STR_BAGS, STR_TONS}, truelight@0: /* LT_DESERT */ {STR_PASSENGERS, STR_LITERS, STR_BAGS, STR_LITERS, STR_TONS, STR_CRATES, STR_TONS, STR_TONS, STR_TONS, STR_LITERS, STR_BAGS, STR_TONS}, truelight@0: /* LT_CANDY */ {STR_PASSENGERS, STR_TONS, STR_BAGS, STR_NOTHING, STR_NOTHING, STR_TONS, STR_TONS, STR_LITERS, STR_TONS, STR_NOTHING, STR_LITERS, STR_NOTHING} truelight@0: }; truelight@0: truelight@0: // get a mask of the allowed currencies depending on the year truelight@0: uint GetMaskOfAllowedCurrencies() truelight@0: { truelight@0: int i; truelight@0: uint mask = 0; truelight@0: for(i=0; i!=lengthof(_currency_specs); i++) { truelight@0: byte flags = _currency_specs[i].flags; truelight@0: if (_cur_year >= (2002-1920) && (flags & CF_TOEURO_2002)) continue; truelight@0: if (_cur_year < (2000-1920) && (flags & CF_ISEURO)) continue; truelight@0: mask |= (1 << i); truelight@0: } truelight@0: return mask; truelight@0: } truelight@0: truelight@0: void CheckSwitchToEuro() truelight@0: { truelight@0: if (_cur_year >= (2002-1920) && _currency_specs[_opt.currency].flags & CF_TOEURO_2002) { truelight@0: _opt.currency = 21; // this is the index of euro above. darkvater@6: AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: static byte *str_cat(byte *dst, const byte *src) truelight@0: { truelight@0: while ( (*dst++ = *src++) != 0) {} truelight@0: return dst - 1; truelight@0: } truelight@0: truelight@0: static byte *GetStringPtr(uint16 string) truelight@0: { truelight@0: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)]; truelight@0: } truelight@0: truelight@0: byte *GetString(byte *buffr, uint16 string) truelight@0: { truelight@0: uint index = string & 0x7FF; truelight@0: uint tab = string >> 11; truelight@0: truelight@0: if (string == 0) error("!invalid string id 0 in GetString"); truelight@0: truelight@0: if ( tab == 4 && index >= 0xC0) truelight@0: return GetSpecialTownNameString(buffr, index - 0xC0); truelight@0: truelight@0: if ( tab == 6 && index == 0xD1) truelight@0: return StationGetSpecialString(buffr); truelight@0: truelight@0: if ( tab == 14 && index >= 0xE4) truelight@0: return GetSpecialPlayerNameString(buffr, index - 0xE4); truelight@0: truelight@0: if ( tab == 15) truelight@0: return GetName(index, buffr); truelight@0: truelight@0: // tab 31 is used for special or dynamic strings truelight@0: if ( tab == 31) { truelight@0: return DecodeString(buffr, index == (STR_SPEC_SCREENSHOT_NAME & 0x7FF) ? _screenshot_name : _userstring); truelight@0: } truelight@0: truelight@0: if (index >= _langtab_num[tab]) truelight@0: error("!String 0x%X is invalid. Probably because an old version of the .lng file.\n", string); truelight@0: truelight@0: return DecodeString(buffr, GetStringPtr(string)); truelight@0: } truelight@0: truelight@193: 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: truelight@0: int32 GetParamInt32() truelight@0: { truelight@0: int32 result = GET_DPARAM32(0); truelight@0: memcpy_overlapping(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: int64 GetParamInt64() truelight@0: { truelight@0: int64 result = GET_DPARAM32(0) + ((uint64)GET_DPARAM32(1) << 32); truelight@0: memcpy_overlapping(&_decode_parameters[0], &_decode_parameters[2], sizeof(uint32) * (lengthof(_decode_parameters)-2)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: truelight@0: int GetParamInt16() truelight@0: { truelight@0: int result = (int16)GET_DPARAM16(0); truelight@0: memcpy_overlapping(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: int GetParamInt8() truelight@0: { truelight@0: int result = (int8)GET_DPARAM8(0); truelight@0: memcpy_overlapping(&_decode_parameters[0], &_decode_parameters[1], sizeof(uint32) * (lengthof(_decode_parameters)-1)); truelight@0: return result; truelight@0: } truelight@0: truelight@0: int GetParamUint16() truelight@0: { truelight@0: int result = GET_DPARAM16(0); truelight@0: memcpy_overlapping(&_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: truelight@0: static byte *FormatCommaNumber(byte *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; truelight@0: 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: } truelight@0: if (tot|=quot || i==9) { truelight@0: *buff++ = (byte)('0' + quot); truelight@0: if (i==0 || i==3 || i==6) *buff++ = ','; truelight@0: } truelight@0: } truelight@0: truelight@0: *buff = 0; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: truelight@0: static byte *FormatNoCommaNumber(byte *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; truelight@0: 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: } truelight@0: if (tot|=quot || i==9) { truelight@0: *buff++ = (byte)('0' + quot); truelight@0: } truelight@0: } truelight@0: truelight@0: *buff = 0; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: truelight@0: truelight@0: static byte *FormatYmdString(byte *buff, uint16 number) truelight@0: { truelight@0: const byte *src; truelight@0: YearMonthDay ymd; truelight@0: truelight@0: ConvertDayToYMD(&ymd, number); truelight@0: truelight@0: 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: truelight@0: return FormatNoCommaNumber(buff+4, ymd.year + 1920); truelight@0: } truelight@0: truelight@0: static byte *FormatMonthAndYear(byte *buff, uint16 number) truelight@0: { truelight@0: const char *src; truelight@0: YearMonthDay ymd; truelight@0: truelight@0: ConvertDayToYMD(&ymd, number); truelight@0: truelight@0: for(src = GetStringPtr(STR_MONTH_JAN + ymd.month); (*buff++=*src++) != 0;) {} truelight@0: buff[-1] = ' '; truelight@0: truelight@0: return FormatNoCommaNumber(buff, ymd.year + 1920); truelight@0: } truelight@0: truelight@26: uint GetCurrentCurrencyRate() { truelight@26: return (&_currency_specs[_opt.currency])->rate; truelight@26: } truelight@26: truelight@0: static byte *FormatGenericCurrency(byte *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 truelight@0: if (number < 0) { *buff++ = '-'; number = -number; } truelight@193: truelight@0: // add pre part truelight@0: s = spec->pre; truelight@0: while (s != spec->pre + lengthof(spec->pre) && (c=*s++)) *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 { truelight@0: if (--j == 0) { *p++ = spec->separator; j = 3; } 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: truelight@0: // add post part truelight@0: s = spec->post; truelight@0: while (s != spec->post + lengthof(spec->post) && (c=*s++)) *buff++ = c; truelight@0: truelight@0: return buff; truelight@0: } truelight@0: truelight@0: static byte *DecodeString(byte *buff, const byte *str) truelight@0: { truelight@0: byte b; truelight@0: truelight@0: while((b = *str++) != 0) { truelight@0: 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: truelight@0: 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} */ truelight@0: #ifdef WITH_REV truelight@0: buff = str_cat(buff, (const byte*)_openttd_revision); truelight@0: #endif 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 truelight@0: char *s; truelight@0: uint16 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 truelight@0: buff = FormatCommaNumber(buff, GetParamInt16() * multiplier ); truelight@0: s = GetStringPtr(cargo_str); truelight@193: truelight@0: memcpy(buff++, " ", 1); truelight@0: while (*s) *buff++ = *s++; darkvater@236: } 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} truelight@0: 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. truelight@0: if (GET_DPARAM16(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; truelight@0: InjectDparam(1); truelight@0: st = DEREF_STATION(GET_DPARAM16(1)); darkvater@64: if (!st->xy) { // station doesn't exist anymore darkvater@64: buff = GetString(buff, STR_UNKNOWN_DESTINATION); darkvater@64: break; darkvater@64: } truelight@0: SET_DPARAM16(0, st->town->townnametype); truelight@0: SET_DPARAM32(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@0: t = DEREF_TOWN(GET_DPARAM16(0)); truelight@0: assert(t->xy); truelight@0: SET_DPARAM32(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} darkvater@395: Waypoint *cp = &_waypoints[GET_DPARAM16(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 { truelight@0: InjectDparam(1); truelight@0: SET_DPARAM16(1, idx + 1); darkvater@395: str = STR_WAYPOINTNAME_CITY_SERIAL; truelight@0: } truelight@0: SET_DPARAM16(0, cp->town_or_string & 0xFF); truelight@0: } truelight@0: truelight@0: buff = GetString(buff, str); truelight@0: } break; 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: } truelight@0: buff[0] = 0; truelight@0: return buff; truelight@0: } truelight@0: truelight@0: truelight@0: static byte *StationGetSpecialString(byte *buff) truelight@0: { truelight@0: int x = GetParamInt8(); truelight@0: if (x & 1) *buff++ = 0xB4; truelight@0: if (x & 2) *buff++ = 0xB5; truelight@0: if (x & 4) *buff++ = 0xB6; truelight@0: if (x & 8) *buff++ = 0xB7; truelight@0: if (x & 16) *buff++ = 0xB8; truelight@0: *buff = 0; truelight@0: return buff; truelight@0: } truelight@0: truelight@0: static byte *GetSpecialTownNameString(byte *buff, int ind) { truelight@0: uint32 x = GetParamInt32(); truelight@193: truelight@0: _town_name_generators[ind](buff, x); truelight@0: truelight@0: 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: truelight@0: static const byte _initial_name_letters[] = { truelight@0: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', truelight@0: }; truelight@0: truelight@0: static byte *GenAndCoName(byte *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: truelight@0: static byte *GenPlayerName_4(byte *buff) truelight@0: { truelight@0: uint32 x = GetParamInt32(); truelight@0: uint i, base, num; truelight@0: truelight@0: 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: truelight@0: static byte *GetSpecialPlayerNameString(byte *buff, int ind) truelight@0: { truelight@0: switch(ind) { truelight@193: truelight@0: // not used truelight@0: case 1: { truelight@0: int i = GetParamInt32() & 0xFFFF; truelight@0: return str_cat(buff, _silly_company_names[i]); truelight@0: } truelight@0: truelight@0: case 2: // used for Foobar & Co company names truelight@0: return GenAndCoName(buff); truelight@0: truelight@0: case 3: // President name truelight@0: return GenPlayerName_4(buff); truelight@0: truelight@0: // song names truelight@0: case 4: { truelight@0: const char *song = _song_names[GetParamUint16() - 1]; truelight@0: return str_cat(buff, song); truelight@0: } truelight@0: } truelight@0: truelight@0: // town name? truelight@0: 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); truelight@0: return str_cat(buff, i == _dynlang.curr ? ((LanguagePackHeader*)_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: 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: { truelight@0: if (s == 0x7000) s = STR_SV_UNNAMED; truelight@0: else if (s == 0x0006) s = STR_SV_EMPTY; truelight@0: else if (s == 0x8864) s = STR_SV_TRAIN_NAME; truelight@0: else if (s == 0x902B) s = STR_SV_ROADVEH_NAME; truelight@0: else if (s == 0x9830) s = STR_SV_SHIP_NAME; truelight@0: else if (s == 0xA02F) s = STR_SV_AIRCRAFT_NAME; truelight@0: else if (IS_INT_INSIDE(s, 0x300F, 0x3030)) s = s - 0x300F + STR_SV_STNAME; truelight@0: else if (s == 0x70E4 || s == 0x70E9) s = SPECSTR_PLAYERNAME_ENGLISH; truelight@0: return s; truelight@0: } truelight@0: truelight@0: bool ReadLanguagePack(int lang_index) { truelight@0: int tot_count, i; truelight@0: byte *lang_pack; truelight@0: size_t len; truelight@0: byte **langpack_offs; truelight@0: byte *s; truelight@0: truelight@0: #define HDR ((LanguagePackHeader*)lang_pack) 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; truelight@193: if (len < sizeof(LanguagePackHeader) || truelight@0: HDR->ident != TO_LE32(LANGUAGE_PACK_IDENT) || truelight@0: HDR->version != TO_LE32(LANGUAGE_PACK_VERSION)) { truelight@0: free(lang_pack); truelight@0: return false; truelight@0: } truelight@0: #undef HDR truelight@193: truelight@0: #if defined(TTD_BIG_ENDIAN) truelight@0: for(i=0; i!=32; i++) { truelight@0: ((LanguagePackHeader*)lang_pack)->offsets[i] = READ_LE_UINT16(&((LanguagePackHeader*)lang_pack)->offsets[i]); truelight@0: } truelight@0: #endif truelight@0: truelight@0: tot_count = 0; truelight@0: for(i=0; i!=32; i++) { truelight@0: uint num = ((LanguagePackHeader*)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 truelight@0: langpack_offs = (byte**)malloc(tot_count * sizeof(byte*)); truelight@0: truelight@0: // Fill offsets truelight@0: s = lang_pack + sizeof(LanguagePackHeader); truelight@0: for(i=0; i!=tot_count; i++) { truelight@0: len = *s; truelight@0: *s++ = 0; // zero terminate the string before. truelight@0: if (len >= 0xC0) { len = ((len & 0x3F) << 8) + *s++; } truelight@0: langpack_offs[i] = s; truelight@0: s += len; truelight@0: } truelight@0: truelight@0: if (_langpack) free(_langpack); truelight@0: _langpack = lang_pack; truelight@0: truelight@0: if (_langpack_offs) 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. truelight@0: void InitializeLanguagePacks() truelight@0: { truelight@0: DynamicLanguages *dl = &_dynlang; truelight@0: int i, j, n, m,def; truelight@0: LanguagePackHeader 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. truelight@0: for(i=m=0; i!=n; i++) { truelight@0: char *s = str_fmt("%s%s", _path.lang_dir, files[i]); truelight@0: in = fopen(s, "rb"); truelight@0: free(s); truelight@193: if (!in || 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: truelight@0: if (!strcmp(hdr.name, "English")) 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; truelight@0: 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: truelight@0: for(i=0; i!=dl->num; i++) truelight@0: if (!strcmp(dl->ent[i].file, dl->curr_file)) { 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: }