2005-07-24 14:12:37 +00:00
/* $Id$ */
2007-04-04 01:35:16 +00:00
/** @file strings.cpp */
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
2005-06-02 19:30:21 +00:00
# include "openttd.h"
2005-08-06 14:59:54 +00:00
# include "currency.h"
2005-02-05 23:03:12 +00:00
# include "namegen.h"
2004-08-09 17:04:08 +00:00
# include "station.h"
# include "town.h"
2004-08-10 15:00:15 +00:00
# include "news.h"
2004-11-15 19:25:59 +00:00
# include "screenshot.h"
2005-03-24 17:03:37 +00:00
# include "waypoint.h"
2005-07-15 18:30:13 +00:00
# include "industry.h"
2005-07-21 22:15:02 +00:00
# include "variables.h"
2006-04-21 03:00:20 +00:00
# include "newgrf_text.h"
2006-07-07 02:44:51 +00:00
# include "music.h"
2006-10-24 19:19:25 +00:00
# include "industry.h"
2007-06-17 15:48:57 +00:00
# include "fileio.h"
2007-02-20 22:09:21 +00:00
# include "cargotype.h"
2007-05-19 09:40:18 +00:00
# include "group.h"
# include "debug.h"
2007-06-18 23:00:55 +00:00
# include "newgrf_townname.h"
2007-06-25 10:40:56 +00:00
# include "signs.h"
2007-06-25 14:46:32 +00:00
# include "newgrf_engine.h"
2007-12-16 19:21:28 +00:00
# include "spritecache.h"
2007-12-16 18:38:19 +00:00
# include "fontcache.h"
# include "gui.h"
2007-12-21 19:49:27 +00:00
# include "strings_func.h"
2007-12-25 11:26:07 +00:00
# include "functions.h"
2007-12-22 21:01:49 +00:00
# include "core/endian_func.hpp"
2007-12-26 13:50:40 +00:00
# include "date_func.h"
2007-12-27 13:35:39 +00:00
# include "vehicle_base.h"
2008-01-07 14:23:25 +00:00
# include "string_func.h"
2008-01-12 14:10:35 +00:00
# include "player_func.h"
# include "player_base.h"
2008-01-13 01:21:35 +00:00
# include "fios.h"
2008-01-13 14:37:30 +00:00
# include "settings_type.h"
2004-08-09 17:04:08 +00:00
2008-01-13 01:21:35 +00:00
# include "table/strings.h"
# include "table/control_codes.h"
2006-08-04 23:45:20 +00:00
2007-03-17 22:21:05 +00:00
DynamicLanguages _dynlang ;
2005-07-26 19:04:19 +00:00
char _userstring [ 128 ] ;
2007-07-16 09:16:58 +00:00
uint64 _decode_parameters [ 20 ] ;
2005-07-26 19:04:19 +00:00
2006-10-21 23:31:34 +00:00
static char * StationGetSpecialString ( char * buff , int x , const char * last ) ;
static char * GetSpecialTownNameString ( char * buff , int ind , uint32 seed , const char * last ) ;
2007-06-21 17:25:17 +00:00
static char * GetSpecialPlayerNameString ( char * buff , int ind , const int64 * argv , const char * last ) ;
2004-08-09 17:04:08 +00:00
2007-06-21 17:25:17 +00:00
static char * FormatString ( char * buff , const char * str , const int64 * argv , uint casei , const char * last ) ;
2004-08-09 17:04:08 +00:00
2007-03-07 12:11:48 +00:00
struct LanguagePack {
2007-04-04 01:35:16 +00:00
uint32 ident ; // 32-bits identifier
2006-08-28 18:53:03 +00:00
uint32 version ; // 32-bits of auto generated version info which is basically a hash of strings.h
char name [ 32 ] ; // the international name of this language
char own_name [ 32 ] ; // the localized name of this language
char isocode [ 16 ] ; // the ISO code for the language (not country code)
uint16 offsets [ 32 ] ; // the offsets
byte plural_form ; // how to compute plural forms
byte pad [ 3 ] ; // pad header to be a multiple of 4
2007-04-04 01:35:16 +00:00
char data [ VARARRAY_SIZE ] ; // list of strings
2007-03-07 12:11:48 +00:00
} ;
2005-02-06 14:47:56 +00:00
static char * * _langpack_offs ;
static LanguagePack * _langpack ;
2007-04-04 01:35:16 +00:00
static uint _langtab_num [ 32 ] ; // Offset into langpack offs
2005-02-06 14:47:56 +00:00
static uint _langtab_start [ 32 ] ; // Offset into langpack offs
2004-08-09 17:04:08 +00:00
2007-04-04 01:35:16 +00:00
/** Read an int64 from the argv array. */
2007-06-21 17:25:17 +00:00
static inline int64 GetInt64 ( const int64 * * argv )
2005-07-15 14:53:44 +00:00
{
assert ( argv ) ;
2007-06-21 17:25:17 +00:00
return * ( * argv ) + + ;
2005-07-15 14:53:44 +00:00
}
2007-04-04 01:35:16 +00:00
/** Read an int32 from the argv array. */
2007-06-21 17:25:17 +00:00
static inline int32 GetInt32 ( const int64 * * argv )
2005-07-15 14:53:44 +00:00
{
2007-06-21 17:25:17 +00:00
return ( int32 ) GetInt64 ( argv ) ;
2005-07-15 14:53:44 +00:00
}
2007-04-04 01:35:16 +00:00
/** Read an array from the argv array. */
2007-06-21 17:25:17 +00:00
static inline const int64 * GetArgvPtr ( const int64 * * argv , int n )
2005-07-15 14:53:44 +00:00
{
2007-06-21 17:25:17 +00:00
const int64 * result ;
2005-07-15 14:53:44 +00:00
assert ( * argv ) ;
result = * argv ;
( * argv ) + = n ;
return result ;
}
2005-07-14 09:43:59 +00:00
# define NUM_BOUND_STRINGS 8
2007-04-04 01:35:16 +00:00
/* Array to hold the bound strings. */
2005-07-14 09:43:59 +00:00
static const char * _bound_strings [ NUM_BOUND_STRINGS ] ;
2007-04-04 01:35:16 +00:00
/* This index is used to implement a "round-robin" allocating of
* slots for BindCString . NUM_BOUND_STRINGS slots are reserved .
* Which means that after NUM_BOUND_STRINGS calls to BindCString ,
* the indices will be reused . */
2005-07-14 09:43:59 +00:00
static int _bind_index ;
2005-02-06 16:56:04 +00:00
static const char * GetStringPtr ( StringID string )
2004-08-09 17:04:08 +00:00
{
return _langpack_offs [ _langtab_start [ string > > 11 ] + ( string & 0x7FF ) ] ;
}
2007-04-04 01:35:16 +00:00
/** The highest 8 bits of string contain the "case index".
* These 8 bits will only be set when FormatString wants to print
* the string in a different case . No one else except FormatString
* should set those bits , therefore string CANNOT be StringID , but uint32 .
* @ param buffr
* @ param string
* @ param argv
* @ param last
* @ return a formatted string of char
*/
2007-06-21 17:25:17 +00:00
static char * GetStringWithArgs ( char * buffr , uint string , const int64 * argv , const char * last )
2004-08-09 17:04:08 +00:00
{
2007-10-30 18:32:26 +00:00
if ( GB ( string , 0 , 16 ) = = 0 ) return GetStringWithArgs ( buffr , STR_UNDEFINED , argv , last ) ;
2005-07-20 15:29:28 +00:00
uint index = GB ( string , 0 , 11 ) ;
uint tab = GB ( string , 11 , 5 ) ;
2006-10-03 09:25:42 +00:00
char buff [ 512 ] ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
switch ( tab ) {
case 4 :
2005-07-15 14:53:44 +00:00
if ( index > = 0xC0 )
2006-10-21 23:31:34 +00:00
return GetSpecialTownNameString ( buffr , index - 0xC0 , GetInt32 ( & argv ) , last ) ;
2005-02-06 16:56:04 +00:00
break ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
case 14 :
2005-07-15 14:53:44 +00:00
if ( index > = 0xE4 )
2006-10-21 23:31:34 +00:00
return GetSpecialPlayerNameString ( buffr , index - 0xE4 , argv , last ) ;
2005-02-06 16:56:04 +00:00
break ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
case 15 :
2008-01-12 19:58:06 +00:00
error ( " Boo! " ) ;
2004-08-09 17:04:08 +00:00
2006-10-03 09:25:42 +00:00
case 26 :
/* Include string within newgrf text (format code 81) */
2007-11-19 21:02:30 +00:00
if ( HasBit ( index , 10 ) ) {
2006-10-03 09:25:42 +00:00
StringID string = GetGRFStringID ( 0 , 0xD000 + GB ( index , 0 , 10 ) ) ;
2006-10-21 23:31:34 +00:00
return GetStringWithArgs ( buffr , string , argv , last ) ;
2006-10-03 09:25:42 +00:00
}
break ;
2006-04-21 03:00:20 +00:00
case 28 :
2006-10-22 00:05:19 +00:00
GetGRFString ( buff , index , lastof ( buff ) ) ;
2006-10-21 23:31:34 +00:00
return FormatString ( buffr , buff , argv , 0 , last ) ;
2006-04-21 03:00:20 +00:00
case 29 :
2006-10-22 00:05:19 +00:00
GetGRFString ( buff , index + 0x800 , lastof ( buff ) ) ;
2006-10-21 23:31:34 +00:00
return FormatString ( buffr , buff , argv , 0 , last ) ;
2006-04-21 03:00:20 +00:00
case 30 :
2006-10-22 00:05:19 +00:00
GetGRFString ( buff , index + 0x1000 , lastof ( buff ) ) ;
2006-10-21 23:31:34 +00:00
return FormatString ( buffr , buff , argv , 0 , last ) ;
2006-04-21 03:00:20 +00:00
2005-07-15 14:53:44 +00:00
case 31 :
2007-04-04 01:35:16 +00:00
/* dynamic strings. These are NOT to be passed through the formatter,
* but passed through verbatim . */
2005-07-14 09:43:59 +00:00
if ( index < ( STR_SPEC_USERSTRING & 0x7FF ) ) {
2006-10-21 23:31:34 +00:00
return strecpy ( buffr , _bound_strings [ index ] , last ) ;
2005-07-14 09:43:59 +00:00
}
2004-08-09 17:04:08 +00:00
2006-10-21 23:31:34 +00:00
return FormatString ( buffr , _userstring , NULL , 0 , last ) ;
2004-08-09 17:04:08 +00:00
}
2005-11-14 19:48:04 +00:00
if ( index > = _langtab_num [ tab ] ) {
2005-02-06 16:56:04 +00:00
error (
" !String 0x%X is invalid. "
" Probably because an old version of the .lng file. \n " , string
) ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
2006-10-21 23:31:34 +00:00
return FormatString ( buffr , GetStringPtr ( GB ( string , 0 , 16 ) ) , argv , GB ( string , 24 , 8 ) , last ) ;
2005-07-15 14:53:44 +00:00
}
2006-10-21 23:31:34 +00:00
char * GetString ( char * buffr , StringID string , const char * last )
2005-07-15 14:53:44 +00:00
{
2007-06-21 17:25:17 +00:00
return GetStringWithArgs ( buffr , string , ( int64 * ) _decode_parameters , last ) ;
2004-08-09 17:04:08 +00:00
}
2005-07-15 14:53:44 +00:00
2006-11-16 22:05:33 +00:00
char * InlineString ( char * buf , StringID string )
{
buf + = Utf8Encode ( buf , SCC_STRING_ID ) ;
buf + = Utf8Encode ( buf , string ) ;
return buf ;
}
2007-02-26 00:36:57 +00:00
/**
* This function takes a C - string and allocates a temporary string ID .
* The StringID of the bound string is valid until BindCString is called
* another NUM_BOUND_STRINGS times . So be careful when using it .
2007-04-04 01:35:16 +00:00
* @ param str temp string to add
* @ return the id of that temp string
2007-02-26 00:36:57 +00:00
* @ note formatting a DATE_TINY calls BindCString twice , thus reduces the
* amount of ' user ' bound strings by 2.
* @ todo rewrite the BindCString system to make the limit flexible and
* non - round - robin . For example by using smart pointers that free
* the allocated StringID when they go out - of - scope / are freed .
*/
2005-07-14 09:43:59 +00:00
StringID BindCString ( const char * str )
{
int idx = ( + + _bind_index ) & ( NUM_BOUND_STRINGS - 1 ) ;
_bound_strings [ idx ] = str ;
return idx + STR_SPEC_DYNSTRING ;
}
2007-04-04 01:35:16 +00:00
/** This function is used to "bind" a C string to a OpenTTD dparam slot.
* @ param n slot of the string
* @ param str string to bind
*/
2005-07-14 09:43:59 +00:00
void SetDParamStr ( uint n , const char * str )
{
SetDParam ( n , BindCString ( str ) ) ;
}
2005-02-06 08:18:00 +00:00
void InjectDParam ( int amount )
2004-08-09 17:04:08 +00:00
{
2007-06-21 19:08:47 +00:00
memmove ( _decode_parameters + amount , _decode_parameters , sizeof ( _decode_parameters ) - amount * sizeof ( uint64 ) ) ;
2004-08-09 17:04:08 +00:00
}
2006-10-21 23:31:34 +00:00
// TODO
2007-07-29 12:13:22 +00:00
static char * FormatCommaNumber ( char * buff , int64 number , const char * last )
2004-08-09 17:04:08 +00:00
{
2007-07-29 12:13:22 +00:00
uint64 divisor = 10000000000000000000ULL ;
uint64 quot ;
2004-08-09 17:04:08 +00:00
int i ;
2007-07-29 12:13:22 +00:00
uint64 tot ;
uint64 num ;
2004-08-09 17:04:08 +00:00
if ( number < 0 ) {
* buff + + = ' - ' ;
number = - number ;
}
num = number ;
tot = 0 ;
2007-07-29 12:13:22 +00:00
for ( i = 0 ; i < 20 ; i + + ) {
2004-08-09 17:04:08 +00:00
quot = 0 ;
2007-07-29 12:13:22 +00:00
if ( num > = divisor ) {
quot = num / divisor ;
num = num % divisor ;
2004-08-09 17:04:08 +00:00
}
2007-07-29 12:13:22 +00:00
if ( tot | = quot | | i = = 19 ) {
2005-02-06 09:52:06 +00:00
* buff + + = ' 0 ' + quot ;
2007-07-29 12:13:22 +00:00
if ( ( i % 3 ) = = 1 & & i ! = 19 ) * buff + + = ' , ' ;
2004-08-09 17:04:08 +00:00
}
2007-07-29 12:13:22 +00:00
divisor / = 10 ;
2004-08-09 17:04:08 +00:00
}
2005-02-06 11:23:41 +00:00
* buff = ' \0 ' ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2006-10-21 23:31:34 +00:00
// TODO
2007-07-29 12:13:22 +00:00
static char * FormatNoCommaNumber ( char * buff , int64 number , const char * last )
2004-08-09 17:04:08 +00:00
{
2007-07-29 12:13:22 +00:00
uint64 divisor = 10000000000000000000ULL ;
uint64 quot ;
2004-08-09 17:04:08 +00:00
int i ;
2007-07-29 12:13:22 +00:00
uint64 tot ;
uint64 num ;
2004-08-09 17:04:08 +00:00
if ( number < 0 ) {
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , " - " , last ) ;
2004-08-09 17:04:08 +00:00
number = - number ;
}
num = number ;
tot = 0 ;
2007-07-29 12:13:22 +00:00
for ( i = 0 ; i < 20 ; i + + ) {
2004-08-09 17:04:08 +00:00
quot = 0 ;
2007-07-29 12:13:22 +00:00
if ( num > = divisor ) {
quot = num / divisor ;
num = num % divisor ;
2004-08-09 17:04:08 +00:00
}
2007-07-29 12:13:22 +00:00
if ( tot | = quot | | i = = 19 ) {
2005-02-06 09:52:06 +00:00
* buff + + = ' 0 ' + quot ;
2004-08-09 17:04:08 +00:00
}
2007-07-29 12:13:22 +00:00
divisor / = 10 ;
2004-08-09 17:04:08 +00:00
}
2005-02-06 11:23:41 +00:00
* buff = ' \0 ' ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2006-10-21 23:31:34 +00:00
static char * FormatYmdString ( char * buff , Date date , const char * last )
2004-08-09 17:04:08 +00:00
{
YearMonthDay ymd ;
2006-08-15 16:55:40 +00:00
ConvertDateToYMD ( date , & ymd ) ;
2004-08-09 17:04:08 +00:00
2007-06-21 17:25:17 +00:00
int64 args [ 3 ] = { ymd . day + STR_01AC_1ST - 1 , STR_0162_JAN + ymd . month , ymd . year } ;
2007-02-26 00:36:57 +00:00
return FormatString ( buff , GetStringPtr ( STR_DATE_LONG ) , args , 0 , last ) ;
2004-08-09 17:04:08 +00:00
}
2006-10-21 23:31:34 +00:00
static char * FormatMonthAndYear ( char * buff , Date date , const char * last )
2004-08-09 17:04:08 +00:00
{
YearMonthDay ymd ;
2006-08-15 16:55:40 +00:00
ConvertDateToYMD ( date , & ymd ) ;
2004-08-09 17:04:08 +00:00
2007-06-21 17:25:17 +00:00
int64 args [ 2 ] = { STR_MONTH_JAN + ymd . month , ymd . year } ;
2007-02-26 00:36:57 +00:00
return FormatString ( buff , GetStringPtr ( STR_DATE_SHORT ) , args , 0 , last ) ;
2004-08-09 17:04:08 +00:00
}
2006-10-21 23:31:34 +00:00
static char * FormatTinyDate ( char * buff , Date date , const char * last )
2005-01-22 23:13:20 +00:00
{
YearMonthDay ymd ;
2006-08-15 16:55:40 +00:00
ConvertDateToYMD ( date , & ymd ) ;
2005-01-22 23:13:20 +00:00
2007-02-26 00:36:57 +00:00
char day [ 3 ] ;
char month [ 3 ] ;
/* We want to zero-pad the days and months */
snprintf ( day , lengthof ( day ) , " %02i " , ymd . day ) ;
snprintf ( month , lengthof ( month ) , " %02i " , ymd . month + 1 ) ;
2007-06-21 17:25:17 +00:00
int64 args [ 3 ] = { BindCString ( day ) , BindCString ( month ) , ymd . year } ;
2007-02-26 00:36:57 +00:00
return FormatString ( buff , GetStringPtr ( STR_DATE_TINY ) , args , 0 , last ) ;
2005-01-22 23:13:20 +00:00
}
2007-06-21 15:57:14 +00:00
static char * FormatGenericCurrency ( char * buff , const CurrencySpec * spec , Money number , bool compact , const char * last )
2004-08-09 17:04:08 +00:00
{
2007-10-20 14:51:09 +00:00
/* We are going to make number absolute for printing, so
* keep this piece of data as we need it later on */
bool negative = number < 0 ;
const char * multiplier = " " ;
2006-10-21 23:31:34 +00:00
char buf [ 40 ] ;
2007-10-20 14:51:09 +00:00
char * p ;
2004-08-09 17:04:08 +00:00
int j ;
2007-10-20 14:51:09 +00:00
number * = spec - > rate ;
2004-08-09 17:04:08 +00:00
2007-04-04 01:35:16 +00:00
/* convert from negative */
2005-02-06 11:23:41 +00:00
if ( number < 0 ) {
2007-08-07 15:20:31 +00:00
if ( buff + Utf8CharLen ( SCC_RED ) > last ) return buff ;
buff + = Utf8Encode ( buff , SCC_RED ) ;
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , " - " , last ) ;
2005-02-06 11:23:41 +00:00
number = - number ;
}
2004-09-10 19:02:27 +00:00
2006-09-15 02:52:17 +00:00
/* Add prefix part, folowing symbol_pos specification.
* Here , it can can be either 0 ( prefix ) or 2 ( both prefix anf suffix ) .
* The only remaining value is 1 ( suffix ) , so everything that is not 1 */
2006-10-21 23:31:34 +00:00
if ( spec - > symbol_pos ! = 1 ) buff = strecpy ( buff , spec - > prefix , last ) ;
2004-08-09 17:04:08 +00:00
2007-04-04 01:35:16 +00:00
/* for huge numbers, compact the number into k or M */
2004-08-09 17:04:08 +00:00
if ( compact ) {
if ( number > = 1000000000 ) {
number = ( number + 500000 ) / 1000000 ;
2006-10-21 23:31:34 +00:00
multiplier = " M " ;
2004-08-09 17:04:08 +00:00
} else if ( number > = 1000000 ) {
number = ( number + 500 ) / 1000 ;
2006-10-21 23:31:34 +00:00
multiplier = " k " ;
2004-09-10 19:02:27 +00:00
}
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2007-04-04 01:35:16 +00:00
/* convert to ascii number and add commas */
2006-10-21 23:31:34 +00:00
p = endof ( buf ) ;
* - - p = ' \0 ' ;
2004-08-09 17:04:08 +00:00
j = 4 ;
2004-09-10 19:02:27 +00:00
do {
2005-02-06 11:23:41 +00:00
if ( - - j = = 0 ) {
2006-10-21 23:31:34 +00:00
* - - p = spec - > separator ;
2005-02-06 11:23:41 +00:00
j = 3 ;
}
2007-10-20 14:51:09 +00:00
* - - p = ' 0 ' + ( char ) ( number % 10 ) ;
2006-10-21 23:31:34 +00:00
} while ( ( number / = 10 ) ! = 0 ) ;
buff = strecpy ( buff , p , last ) ;
2004-08-09 17:04:08 +00:00
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , multiplier , last ) ;
2004-08-09 17:04:08 +00:00
2006-09-15 02:52:17 +00:00
/* Add suffix part, folowing symbol_pos specification.
* Here , it can can be either 1 ( suffix ) or 2 ( both prefix anf suffix ) .
* The only remaining value is 1 ( prefix ) , so everything that is not 0 */
2006-10-21 23:31:34 +00:00
if ( spec - > symbol_pos ! = 0 ) buff = strecpy ( buff , spec - > suffix , last ) ;
2004-08-09 17:04:08 +00:00
2007-10-20 14:51:09 +00:00
if ( negative ) {
2007-08-07 15:20:31 +00:00
if ( buff + Utf8CharLen ( SCC_PREVIOUS_COLOUR ) > last ) return buff ;
buff + = Utf8Encode ( buff , SCC_PREVIOUS_COLOUR ) ;
* buff = ' \0 ' ;
}
2004-08-09 17:04:08 +00:00
return buff ;
}
2007-07-29 12:13:22 +00:00
static int DeterminePluralForm ( int64 cnt )
2005-07-16 17:12:32 +00:00
{
2007-07-29 12:13:22 +00:00
uint64 n = cnt ;
2007-04-04 01:35:16 +00:00
/* The absolute value determines plurality */
2007-07-29 12:13:22 +00:00
if ( cnt < 0 ) n = - cnt ;
2005-07-16 17:12:32 +00:00
2006-02-01 07:36:15 +00:00
switch ( _langpack - > plural_form ) {
2007-04-04 01:35:16 +00:00
/* Two forms, singular used for one only
* Used in :
* Danish , Dutch , English , German , Norwegian , Swedish , Estonian , Finnish ,
* Greek , Hebrew , Italian , Portuguese , Spanish , Esperanto */
2005-07-16 17:12:32 +00:00
case 0 :
default :
return n ! = 1 ;
2007-04-04 01:35:16 +00:00
/* Only one form
* Used in :
* Hungarian , Japanese , Korean , Turkish */
2005-07-16 17:12:32 +00:00
case 1 :
return 0 ;
2007-04-04 01:35:16 +00:00
/* Two forms, singular used for zero and one
* Used in :
* French , Brazilian Portuguese */
2005-07-16 17:12:32 +00:00
case 2 :
return n > 1 ;
2007-04-04 01:35:16 +00:00
/* Three forms, special case for zero
* Used in :
* Latvian */
2005-07-16 17:12:32 +00:00
case 3 :
2007-04-18 22:41:53 +00:00
return n % 10 = = 1 & & n % 100 ! = 11 ? 0 : n ! = 0 ? 1 : 2 ;
2005-07-16 17:12:32 +00:00
2007-04-04 01:35:16 +00:00
/* Three forms, special case for one and two
* Used in :
* Gaelige ( Irish ) */
2005-07-16 17:12:32 +00:00
case 4 :
2007-04-18 22:10:36 +00:00
return n = = 1 ? 0 : n = = 2 ? 1 : 2 ;
2005-07-16 17:12:32 +00:00
2007-04-04 01:35:16 +00:00
/* Three forms, special case for numbers ending in 1[2-9]
* Used in :
* Lithuanian */
2005-07-16 17:12:32 +00:00
case 5 :
2007-04-18 22:41:53 +00:00
return n % 10 = = 1 & & n % 100 ! = 11 ? 0 : n % 10 > = 2 & & ( n % 100 < 10 | | n % 100 > = 20 ) ? 1 : 2 ;
2005-07-16 17:12:32 +00:00
2007-04-04 01:35:16 +00:00
/* Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
* Used in :
* Croatian , Czech , Russian , Slovak , Ukrainian */
2005-07-16 17:12:32 +00:00
case 6 :
2007-04-18 22:41:53 +00:00
return n % 10 = = 1 & & n % 100 ! = 11 ? 0 : n % 10 > = 2 & & n % 10 < = 4 & & ( n % 100 < 10 | | n % 100 > = 20 ) ? 1 : 2 ;
2005-07-16 17:12:32 +00:00
2007-04-04 01:35:16 +00:00
/* Three forms, special case for one and some numbers ending in 2, 3, or 4
* Used in :
* Polish */
2005-07-16 17:12:32 +00:00
case 7 :
2007-04-18 22:41:53 +00:00
return n = = 1 ? 0 : n % 10 > = 2 & & n % 10 < = 4 & & ( n % 100 < 10 | | n % 100 > = 20 ) ? 1 : 2 ;
2005-07-16 17:12:32 +00:00
2007-04-04 01:35:16 +00:00
/* Four forms, special case for one and all numbers ending in 02, 03, or 04
* Used in :
* Slovenian */
2005-07-16 17:12:32 +00:00
case 8 :
2007-04-18 22:41:53 +00:00
return n % 100 = = 1 ? 0 : n % 100 = = 2 ? 1 : n % 100 = = 3 | | n % 100 = = 4 ? 2 : 3 ;
2005-07-16 17:12:32 +00:00
}
}
static const char * ParseStringChoice ( const char * b , uint form , char * dst , int * dstlen )
{
//<NUM> {Length of each string} {each string}
uint n = ( byte ) * b + + ;
2007-04-18 22:10:36 +00:00
uint pos , i , mylen = 0 , mypos = 0 ;
2006-02-01 07:36:15 +00:00
for ( i = pos = 0 ; i ! = n ; i + + ) {
2005-07-16 17:12:32 +00:00
uint len = ( byte ) * b + + ;
if ( i = = form ) {
mypos = pos ;
mylen = len ;
}
pos + = len ;
}
* dstlen = mylen ;
memcpy ( dst , b + mypos , mylen ) ;
return b + pos ;
}
2007-03-07 12:11:48 +00:00
struct Units {
2006-03-26 21:50:57 +00:00
int s_m ; ///< Multiplier for velocity
int s_s ; ///< Shift for velocity
StringID velocity ; ///< String for velocity
int p_m ; ///< Multiplier for power
int p_s ; ///< Shift for power
StringID power ; ///< String for velocity
int w_m ; ///< Multiplier for weight
int w_s ; ///< Shift for weight
StringID s_weight ; ///< Short string for weight
StringID l_weight ; ///< Long string for weight
int v_m ; ///< Multiplier for volume
int v_s ; ///< Shift for volume
StringID s_volume ; ///< Short string for volume
StringID l_volume ; ///< Long string for volume
2006-04-09 18:25:31 +00:00
int f_m ; ///< Multiplier for force
int f_s ; ///< Shift for force
StringID force ; ///< String for force
2007-03-07 12:11:48 +00:00
} ;
2006-03-26 21:50:57 +00:00
2006-04-09 18:25:31 +00:00
/* Unit conversions */
2006-03-26 21:50:57 +00:00
static const Units units [ ] = {
2006-12-28 17:03:36 +00:00
{ // Imperial (Original, mph, hp, metric ton, litre, kN)
2007-01-30 21:10:04 +00:00
1 , 0 , STR_UNITS_VELOCITY_IMPERIAL ,
2006-03-26 21:50:57 +00:00
1 , 0 , STR_UNITS_POWER_IMPERIAL ,
1 , 0 , STR_UNITS_WEIGHT_SHORT_METRIC , STR_UNITS_WEIGHT_LONG_METRIC ,
1000 , 0 , STR_UNITS_VOLUME_SHORT_METRIC , STR_UNITS_VOLUME_LONG_METRIC ,
2006-12-28 17:03:36 +00:00
1 , 0 , STR_UNITS_FORCE_SI ,
2006-03-26 21:50:57 +00:00
} ,
2006-12-28 17:03:36 +00:00
{ // Metric (km/h, hp, metric ton, litre, kN)
2007-01-30 21:10:04 +00:00
103 , 6 , STR_UNITS_VELOCITY_METRIC ,
2006-03-26 21:50:57 +00:00
1 , 0 , STR_UNITS_POWER_METRIC ,
1 , 0 , STR_UNITS_WEIGHT_SHORT_METRIC , STR_UNITS_WEIGHT_LONG_METRIC ,
1000 , 0 , STR_UNITS_VOLUME_SHORT_METRIC , STR_UNITS_VOLUME_LONG_METRIC ,
2006-12-28 17:03:36 +00:00
1 , 0 , STR_UNITS_FORCE_SI ,
2006-03-26 21:50:57 +00:00
} ,
2006-04-09 18:25:31 +00:00
{ // SI (m/s, kilowatt, kilogram, cubic metres, kilonewton)
2007-01-30 21:10:04 +00:00
1831 , 12 , STR_UNITS_VELOCITY_SI ,
2006-03-26 21:50:57 +00:00
764 , 10 , STR_UNITS_POWER_SI ,
1000 , 0 , STR_UNITS_WEIGHT_SHORT_SI , STR_UNITS_WEIGHT_LONG_SI ,
2006-04-09 14:49:45 +00:00
1 , 0 , STR_UNITS_VOLUME_SHORT_SI , STR_UNITS_VOLUME_LONG_SI ,
2006-04-09 18:25:31 +00:00
1 , 0 , STR_UNITS_FORCE_SI ,
2006-03-26 21:50:57 +00:00
} ,
} ;
2005-07-16 17:12:32 +00:00
2007-06-21 17:25:17 +00:00
static char * FormatString ( char * buff , const char * str , const int64 * argv , uint casei , const char * last )
2004-08-09 17:04:08 +00:00
{
2006-10-13 15:52:22 +00:00
extern const char _openttd_revision [ ] ;
2006-11-16 22:05:33 +00:00
WChar b ;
2007-06-21 17:25:17 +00:00
const int64 * argv_orig = argv ;
2005-07-17 10:18:23 +00:00
uint modifier = 0 ;
2004-08-09 17:04:08 +00:00
2006-11-16 22:05:33 +00:00
while ( ( b = Utf8Consume ( & str ) ) ! = ' \0 ' ) {
2007-09-22 23:55:34 +00:00
if ( SCC_NEWGRF_FIRST < = b & & b < = SCC_NEWGRF_LAST ) {
/* We need to pass some stuff as it might be modified; oh boy. */
b = RemapNewGRFStringControlCode ( b , & buff , & str , ( int64 * ) argv ) ;
if ( b = = 0 ) continue ;
}
2005-02-06 11:23:41 +00:00
switch ( b ) {
2006-11-16 22:05:33 +00:00
case SCC_SETX : // {SETX}
if ( buff + Utf8CharLen ( SCC_SETX ) + 1 < last ) {
buff + = Utf8Encode ( buff , SCC_SETX ) ;
* buff + + = * str + + ;
}
break ;
case SCC_SETXY : // {SETXY}
if ( buff + Utf8CharLen ( SCC_SETXY ) + 2 < last ) {
buff + = Utf8Encode ( buff , SCC_SETXY ) ;
* buff + + = * str + + ;
* buff + + = * str + + ;
}
break ;
case SCC_STRING_ID : // {STRINL}
buff = GetStringWithArgs ( buff , Utf8Consume ( & str ) , argv , last ) ;
break ;
case SCC_DATE_LONG : // {DATE_LONG}
buff = FormatYmdString ( buff , GetInt32 ( & argv ) , last ) ;
break ;
case SCC_DATE_SHORT : // {DATE_SHORT}
buff = FormatMonthAndYear ( buff , GetInt32 ( & argv ) , last ) ;
break ;
case SCC_VELOCITY : { // {VELOCITY}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-11-16 22:05:33 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . s_m > > units [ _opt_ptr - > units ] . s_s ;
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . velocity ) , args , modifier > > 24 , last ) ;
modifier = 0 ;
break ;
2006-10-21 23:31:34 +00:00
}
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_CURRENCY_COMPACT : /* {CURRCOMPACT} */
2007-06-21 15:37:05 +00:00
buff = FormatGenericCurrency ( buff , _currency , GetInt64 ( & argv ) , true , last ) ;
2004-08-09 17:04:08 +00:00
break ;
2006-11-16 22:05:33 +00:00
case SCC_REVISION : /* {REV} */
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , _openttd_revision , last ) ;
2004-08-09 17:04:08 +00:00
break ;
2006-11-16 22:05:33 +00:00
case SCC_CARGO_SHORT : { /* {SHORTCARGO} */
2007-04-04 01:35:16 +00:00
/* Short description of cargotypes. Layout:
* 8 - bit = cargo type
* 16 - bit = cargo count */
2007-02-20 22:09:21 +00:00
StringID cargo_str = GetCargo ( GetInt32 ( & argv ) ) - > units_volume ;
2006-03-26 21:50:57 +00:00
switch ( cargo_str ) {
case STR_TONS : {
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . w_m > > units [ _opt_ptr - > units ] . w_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . l_weight ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
case STR_LITERS : {
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . v_m > > units [ _opt_ptr - > units ] . v_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . l_volume ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
default :
2007-03-22 23:19:40 +00:00
if ( cargo_str > = 0xE000 & & cargo_str < 0xF800 ) {
/* NewGRF strings from Action 4 use a different format here,
* of e . g . " x tonnes of coal " , so process accordingly . */
buff = GetStringWithArgs ( buff , cargo_str , argv + + , last ) ;
} else {
buff = FormatCommaNumber ( buff , GetInt32 ( & argv ) , last ) ;
buff = strecpy ( buff , " " , last ) ;
buff = strecpy ( buff , GetStringPtr ( cargo_str ) , last ) ;
}
2006-03-26 21:50:57 +00:00
break ;
}
2005-02-06 11:23:41 +00:00
} break ;
2006-11-16 22:05:33 +00:00
case SCC_STRING1 : { /* {STRING1} */
2007-04-04 01:35:16 +00:00
/* String that consumes ONE argument */
2005-07-17 10:18:23 +00:00
uint str = modifier + GetInt32 ( & argv ) ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , str , GetArgvPtr ( & argv , 1 ) , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 14:53:44 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_STRING2 : { /* {STRING2} */
2007-04-04 01:35:16 +00:00
/* String that consumes TWO arguments */
2005-07-17 10:18:23 +00:00
uint str = modifier + GetInt32 ( & argv ) ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , str , GetArgvPtr ( & argv , 2 ) , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 14:53:44 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_STRING3 : { /* {STRING3} */
2007-04-04 01:35:16 +00:00
/* String that consumes THREE arguments */
2005-07-17 10:18:23 +00:00
uint str = modifier + GetInt32 ( & argv ) ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , str , GetArgvPtr ( & argv , 3 ) , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 14:53:44 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_STRING4 : { /* {STRING4} */
2007-04-04 01:35:16 +00:00
/* String that consumes FOUR arguments */
2005-07-17 10:18:23 +00:00
uint str = modifier + GetInt32 ( & argv ) ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , str , GetArgvPtr ( & argv , 4 ) , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 14:53:44 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_STRING5 : { /* {STRING5} */
2007-04-04 01:35:16 +00:00
/* String that consumes FIVE arguments */
2005-07-17 10:18:23 +00:00
uint str = modifier + GetInt32 ( & argv ) ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , str , GetArgvPtr ( & argv , 5 ) , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 14:53:44 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_STATION_FEATURES : { /* {STATIONFEATURES} */
2006-10-21 23:31:34 +00:00
buff = StationGetSpecialString ( buff , GetInt32 ( & argv ) , last ) ;
2005-07-15 14:53:44 +00:00
break ;
}
2004-08-09 17:04:08 +00:00
2006-11-16 22:05:33 +00:00
case SCC_INDUSTRY_NAME : { /* {INDUSTRY} */
2006-02-18 14:41:24 +00:00
const Industry * i = GetIndustry ( GetInt32 ( & argv ) ) ;
2007-06-21 17:25:17 +00:00
int64 args [ 2 ] ;
2005-07-15 18:30:13 +00:00
2007-04-04 01:35:16 +00:00
/* industry not valid anymore? */
2007-08-02 23:21:52 +00:00
if ( ! i - > IsValid ( ) ) break ;
2005-07-15 18:30:13 +00:00
2007-04-04 01:35:16 +00:00
/* First print the town name and the industry type name
* The string STR_INDUSTRY_PATTERN controls the formatting */
2005-07-15 18:30:13 +00:00
args [ 0 ] = i - > town - > index ;
2006-10-24 19:19:25 +00:00
args [ 1 ] = GetIndustrySpec ( i - > type ) - > name ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( STR_INDUSTRY_FORMAT ) , args , modifier > > 24 , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-15 18:30:13 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_VOLUME : { // {VOLUME}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . v_m > > units [ _opt_ptr - > units ] . v_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . l_volume ) , args , modifier > > 24 , last ) ;
2005-07-17 10:18:23 +00:00
modifier = 0 ;
2005-07-16 20:58:04 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_GENDER_LIST : { // {G 0 Der Die Das}
2007-11-12 20:05:12 +00:00
char buffr [ 512 ] ;
const char * s = GetStringWithArgs ( buffr , argv_orig [ ( byte ) * str + + ] , argv , last ) ; // contains the string that determines gender.
2005-07-16 20:58:04 +00:00
int len ;
int gender = 0 ;
2007-08-05 14:08:38 +00:00
if ( s ! = NULL ) {
wchar_t c = Utf8Consume ( & s ) ;
/* Switch case is always put before genders, so remove those bits */
if ( c = = SCC_SWITCH_CASE ) {
/* Skip to the last (i.e. default) case */
for ( uint num = ( byte ) * s + + ; num ! = 0 ; num - - ) s + = 3 + ( s [ 1 ] < < 8 ) + s [ 2 ] ;
c = Utf8Consume ( & s ) ;
}
/* Does this string have a gender, if so, set it */
if ( c = = SCC_GENDER_INDEX ) gender = ( byte ) s [ 0 ] ;
}
2005-07-16 20:58:04 +00:00
str = ParseStringChoice ( str , gender , buff , & len ) ;
buff + = len ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_DATE_TINY : { // {DATE_TINY}
2006-10-21 23:31:34 +00:00
buff = FormatTinyDate ( buff , GetInt32 ( & argv ) , last ) ;
2005-07-17 10:18:23 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_CARGO : { // {CARGO}
2007-04-04 01:35:16 +00:00
/* Layout now is:
* 8 bit - cargo type
* 16 - bit - cargo count */
2006-10-20 19:48:25 +00:00
CargoID cargo = GetInt32 ( & argv ) ;
2007-08-03 09:08:49 +00:00
StringID cargo_str = ( cargo = = CT_INVALID ) ? STR_8838_N_A : GetCargo ( cargo ) - > quantifier ;
2006-10-21 23:31:34 +00:00
buff = GetStringWithArgs ( buff , cargo_str , argv + + , last ) ;
2005-07-17 10:18:23 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_POWER : { // {POWER}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . p_m > > units [ _opt_ptr - > units ] . p_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . power ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_VOLUME_SHORT : { // {VOLUME_S}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . v_m > > units [ _opt_ptr - > units ] . v_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . s_volume ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_WEIGHT : { // {WEIGHT}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . w_m > > units [ _opt_ptr - > units ] . w_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . l_weight ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_WEIGHT_SHORT : { // {WEIGHT_S}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-03-26 21:50:57 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . w_m > > units [ _opt_ptr - > units ] . w_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . s_weight ) , args , modifier > > 24 , last ) ;
2006-03-26 21:50:57 +00:00
modifier = 0 ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_FORCE : { // {FORCE}
2007-06-21 17:25:17 +00:00
int64 args [ 1 ] ;
2006-04-09 18:25:31 +00:00
assert ( _opt_ptr - > units < lengthof ( units ) ) ;
args [ 0 ] = GetInt32 ( & argv ) * units [ _opt_ptr - > units ] . f_m > > units [ _opt_ptr - > units ] . f_s ;
2006-10-21 23:31:34 +00:00
buff = FormatString ( buff , GetStringPtr ( units [ _opt_ptr - > units ] . force ) , args , modifier > > 24 , last ) ;
2006-04-09 18:25:31 +00:00
modifier = 0 ;
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_SKIP : // {SKIP}
argv + + ;
break ;
2005-07-16 20:58:04 +00:00
2007-04-04 01:35:16 +00:00
/* This sets up the gender for the string.
* We just ignore this one . It ' s used in { G 0 Der Die Das } to determine the case . */
2006-11-16 22:05:33 +00:00
case SCC_GENDER_INDEX : // {GENDER 0}
str + + ;
break ;
2004-09-10 19:02:27 +00:00
2006-11-16 22:05:33 +00:00
case SCC_STRING : { // {STRING}
uint str = modifier + GetInt32 ( & argv ) ;
2007-04-04 01:35:16 +00:00
/* WARNING. It's prohibited for the included string to consume any arguments.
* For included strings that consume argument , you should use STRING1 , STRING2 etc .
* To debug stuff you can set argv to NULL and it will tell you */
2006-11-16 22:05:33 +00:00
buff = GetStringWithArgs ( buff , str , argv , last ) ;
modifier = 0 ;
break ;
}
2004-08-09 17:04:08 +00:00
2006-11-16 22:05:33 +00:00
case SCC_COMMA : // {COMMA}
2007-07-29 12:13:22 +00:00
buff = FormatCommaNumber ( buff , GetInt64 ( & argv ) , last ) ;
2006-11-16 22:05:33 +00:00
break ;
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_ARG_INDEX : // Move argument pointer
argv = argv_orig + ( byte ) * str + + ;
break ;
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_PLURAL_LIST : { // {P}
2007-07-29 12:13:22 +00:00
int64 v = argv_orig [ ( byte ) * str + + ] ; // contains the number that determines plural
2006-11-16 22:05:33 +00:00
int len ;
str = ParseStringChoice ( str , DeterminePluralForm ( v ) , buff , & len ) ;
buff + = len ;
break ;
}
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_NUM : // {NUM}
2007-07-29 12:13:22 +00:00
buff = FormatNoCommaNumber ( buff , GetInt64 ( & argv ) , last ) ;
2006-11-16 22:05:33 +00:00
break ;
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_CURRENCY : // {CURRENCY}
2007-06-21 15:37:05 +00:00
buff = FormatGenericCurrency ( buff , _currency , GetInt64 ( & argv ) , false , last ) ;
2006-11-16 22:05:33 +00:00
break ;
2005-09-10 15:14:35 +00:00
2006-11-16 22:05:33 +00:00
case SCC_WAYPOINT_NAME : { // {WAYPOINT}
Waypoint * wp = GetWaypoint ( GetInt32 ( & argv ) ) ;
2008-01-12 19:58:06 +00:00
if ( ! wp - > IsValid ( ) ) { // waypoint doesn't exist anymore
buff = GetStringWithArgs ( buff , STR_UNKNOWN_DESTINATION , NULL , last ) ;
} else if ( wp - > name ! = NULL ) {
buff = strecpy ( buff , wp - > name , last ) ;
2006-11-16 22:05:33 +00:00
} else {
2008-01-12 19:58:06 +00:00
int64 temp [ 2 ] ;
2006-11-16 22:05:33 +00:00
temp [ 0 ] = wp - > town_index ;
temp [ 1 ] = wp - > town_cn + 1 ;
2008-01-12 19:58:06 +00:00
StringID str = wp - > town_cn = = 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL ;
buff = GetStringWithArgs ( buff , str , temp , last ) ;
2006-11-16 22:05:33 +00:00
}
break ;
2005-07-17 10:18:23 +00:00
}
2004-08-09 17:04:08 +00:00
2006-11-16 22:05:33 +00:00
case SCC_STATION_NAME : { // {STATION}
const Station * st = GetStation ( GetInt32 ( & argv ) ) ;
2005-07-15 14:53:44 +00:00
2007-02-13 15:42:52 +00:00
if ( ! st - > IsValid ( ) ) { // station doesn't exist anymore
2006-11-16 22:05:33 +00:00
buff = GetStringWithArgs ( buff , STR_UNKNOWN_DESTINATION , NULL , last ) ;
2008-01-12 19:58:06 +00:00
} else if ( st - > name ! = NULL ) {
buff = strecpy ( buff , st - > name , last ) ;
2006-11-16 22:05:33 +00:00
} else {
2007-06-25 10:22:48 +00:00
int64 temp [ 3 ] ;
2007-06-18 23:00:55 +00:00
temp [ 0 ] = STR_TOWN ;
temp [ 1 ] = st - > town - > index ;
2007-06-25 10:22:48 +00:00
temp [ 2 ] = st - > index ;
2006-11-16 22:05:33 +00:00
buff = GetStringWithArgs ( buff , st - > string_id , temp , last ) ;
}
break ;
2004-08-16 13:54:37 +00:00
}
2005-11-14 19:48:04 +00:00
2006-11-16 22:05:33 +00:00
case SCC_TOWN_NAME : { // {TOWN}
const Town * t = GetTown ( GetInt32 ( & argv ) ) ;
2007-06-21 17:25:17 +00:00
int64 temp [ 1 ] ;
2005-11-13 13:43:55 +00:00
2007-08-02 21:05:54 +00:00
assert ( t - > IsValid ( ) ) ;
2004-08-09 17:04:08 +00:00
2006-11-16 22:05:33 +00:00
temp [ 0 ] = t - > townnameparts ;
2007-06-18 23:00:55 +00:00
uint32 grfid = t - > townnamegrfid ;
2008-01-12 19:58:06 +00:00
if ( t - > name ! = NULL ) {
buff = strecpy ( buff , t - > name , last ) ;
} else if ( grfid = = 0 ) {
2007-06-18 23:00:55 +00:00
/* Original town name */
buff = GetStringWithArgs ( buff , t - > townnametype , temp , last ) ;
} else {
/* Newgrf town name */
if ( GetGRFTownName ( grfid ) ! = NULL ) {
/* The grf is loaded */
buff = GRFTownNameGenerate ( buff , t - > townnamegrfid , t - > townnametype , t - > townnameparts , last ) ;
} else {
/* Fallback to english original */
buff = GetStringWithArgs ( buff , SPECSTR_TOWNNAME_ENGLISH , temp , last ) ;
}
}
2006-11-16 22:05:33 +00:00
break ;
}
2004-08-09 17:04:08 +00:00
2007-05-19 09:40:18 +00:00
case SCC_GROUP_NAME : { // {GROUP}
const Group * g = GetGroup ( GetInt32 ( & argv ) ) ;
2007-08-02 12:51:57 +00:00
assert ( g - > IsValid ( ) ) ;
2007-05-19 09:40:18 +00:00
2008-01-12 19:58:06 +00:00
if ( g - > name ! = NULL ) {
buff = strecpy ( buff , g - > name , last ) ;
} else {
int64 args [ 1 ] ;
2007-05-19 09:40:18 +00:00
2008-01-12 19:58:06 +00:00
args [ 0 ] = g - > index ;
buff = GetStringWithArgs ( buff , STR_GROUP_NAME_FORMAT , args , last ) ;
}
2007-05-19 09:40:18 +00:00
break ;
}
2007-06-25 14:46:32 +00:00
case SCC_ENGINE_NAME : { // {ENGINE}
EngineID engine = ( EngineID ) GetInt32 ( & argv ) ;
2008-01-12 19:58:06 +00:00
const Engine * e = GetEngine ( engine ) ;
2007-06-25 14:46:32 +00:00
2008-01-12 19:58:06 +00:00
if ( e - > name ! = NULL ) {
buff = strecpy ( buff , e - > name , last ) ;
} else {
buff = GetStringWithArgs ( buff , EngInfo ( engine ) - > string_id , NULL , last ) ;
}
2007-06-25 14:46:32 +00:00
break ;
}
2007-06-24 22:42:11 +00:00
case SCC_VEHICLE_NAME : { // {VEHICLE}
const Vehicle * v = GetVehicle ( GetInt32 ( & argv ) ) ;
2008-01-12 19:58:06 +00:00
if ( v - > name ! = NULL ) {
buff = strecpy ( buff , v - > name , last ) ;
} else {
int64 args [ 1 ] ;
args [ 0 ] = v - > unitnumber ;
StringID str ;
switch ( v - > type ) {
default : NOT_REACHED ( ) ;
case VEH_TRAIN : str = STR_SV_TRAIN_NAME ; break ;
case VEH_ROAD : str = STR_SV_ROADVEH_NAME ; break ;
case VEH_SHIP : str = STR_SV_SHIP_NAME ; break ;
case VEH_AIRCRAFT : str = STR_SV_AIRCRAFT_NAME ; break ;
}
2007-06-24 22:42:11 +00:00
2008-01-12 19:58:06 +00:00
buff = GetStringWithArgs ( buff , str , args , last ) ;
}
2007-06-24 22:42:11 +00:00
break ;
}
2007-06-25 10:40:56 +00:00
case SCC_SIGN_NAME : { // {SIGN}
const Sign * si = GetSign ( GetInt32 ( & argv ) ) ;
2008-01-12 19:58:06 +00:00
if ( si - > name ! = NULL ) {
buff = strecpy ( buff , si - > name , last ) ;
} else {
buff = GetStringWithArgs ( buff , STR_280A_SIGN , NULL , last ) ;
}
2007-06-25 10:40:56 +00:00
break ;
}
2007-06-25 13:30:38 +00:00
case SCC_COMPANY_NAME : { // {COMPANY}
const Player * p = GetPlayer ( ( PlayerID ) GetInt32 ( & argv ) ) ;
2008-01-12 19:58:06 +00:00
if ( p - > name ! = NULL ) {
buff = strecpy ( buff , p - > name , last ) ;
} else {
int64 args [ 1 ] ;
args [ 0 ] = p - > name_2 ;
buff = GetStringWithArgs ( buff , p - > name_1 , args , last ) ;
}
2007-06-25 13:30:38 +00:00
break ;
}
case SCC_COMPANY_NUM : { // {COMPANYNUM}
PlayerID player = ( PlayerID ) GetInt32 ( & argv ) ;
/* Nothing is added for AI or inactive players */
if ( IsHumanPlayer ( player ) & & IsValidPlayer ( player ) ) {
int64 args [ 1 ] ;
args [ 0 ] = player + 1 ;
buff = GetStringWithArgs ( buff , STR_7002_PLAYER , args , last ) ;
}
break ;
}
case SCC_PLAYER_NAME : { // {PLAYERNAME}
const Player * p = GetPlayer ( ( PlayerID ) GetInt32 ( & argv ) ) ;
2008-01-12 19:58:06 +00:00
if ( p - > president_name ! = NULL ) {
buff = strecpy ( buff , p - > president_name , last ) ;
} else {
int64 args [ 1 ] ;
args [ 0 ] = p - > president_name_2 ;
buff = GetStringWithArgs ( buff , p - > president_name_1 , args , last ) ;
}
2007-06-25 13:30:38 +00:00
break ;
}
2006-11-16 22:05:33 +00:00
case SCC_SETCASE : { // {SETCASE}
2007-04-04 01:35:16 +00:00
/* This is a pseudo command, it's outputted when someone does {STRING.ack}
* The modifier is added to all subsequent GetStringWithArgs that accept the modifier . */
2006-11-16 22:05:33 +00:00
modifier = ( byte ) * str + + < < 24 ;
break ;
}
case SCC_SWITCH_CASE : { // {Used to implement case switching}
2007-04-04 01:35:16 +00:00
/* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
* Each LEN is printed using 2 bytes in big endian order . */
2006-11-16 22:05:33 +00:00
uint num = ( byte ) * str + + ;
while ( num ) {
if ( ( byte ) str [ 0 ] = = casei ) {
2007-04-04 01:35:16 +00:00
/* Found the case, adjust str pointer and continue */
2006-11-16 22:05:33 +00:00
str + = 3 ;
break ;
}
2007-04-04 01:35:16 +00:00
/* Otherwise skip to the next case */
2006-11-16 22:05:33 +00:00
str + = 3 + ( str [ 1 ] < < 8 ) + str [ 2 ] ;
num - - ;
2005-07-17 10:18:23 +00:00
}
2006-11-16 22:05:33 +00:00
break ;
2005-07-17 10:18:23 +00:00
}
2005-01-22 23:13:20 +00:00
2006-11-16 22:05:33 +00:00
default :
if ( buff + Utf8CharLen ( b ) < last ) buff + = Utf8Encode ( buff , b ) ;
break ;
2004-08-09 17:04:08 +00:00
}
}
2005-02-06 11:23:41 +00:00
* buff = ' \0 ' ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2006-10-21 23:31:34 +00:00
static char * StationGetSpecialString ( char * buff , int x , const char * last )
2004-08-09 17:04:08 +00:00
{
2006-11-16 22:05:33 +00:00
if ( ( x & 0x01 ) & & ( buff + Utf8CharLen ( SCC_TRAIN ) < last ) ) buff + = Utf8Encode ( buff , SCC_TRAIN ) ;
if ( ( x & 0x02 ) & & ( buff + Utf8CharLen ( SCC_LORRY ) < last ) ) buff + = Utf8Encode ( buff , SCC_LORRY ) ;
if ( ( x & 0x04 ) & & ( buff + Utf8CharLen ( SCC_BUS ) < last ) ) buff + = Utf8Encode ( buff , SCC_BUS ) ;
if ( ( x & 0x08 ) & & ( buff + Utf8CharLen ( SCC_PLANE ) < last ) ) buff + = Utf8Encode ( buff , SCC_PLANE ) ;
if ( ( x & 0x10 ) & & ( buff + Utf8CharLen ( SCC_SHIP ) < last ) ) buff + = Utf8Encode ( buff , SCC_SHIP ) ;
* buff = ' \0 ' ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2006-10-21 23:31:34 +00:00
static char * GetSpecialTownNameString ( char * buff , int ind , uint32 seed , const char * last )
2005-02-06 11:23:41 +00:00
{
2006-10-21 23:31:34 +00:00
char name [ 512 ] ;
2004-08-09 17:04:08 +00:00
2006-10-22 15:13:48 +00:00
_town_name_generators [ ind ] ( name , seed , lastof ( name ) ) ;
2006-10-21 23:31:34 +00:00
return strecpy ( buff , name , last ) ;
2004-08-09 17:04:08 +00:00
}
2005-11-16 11:17:52 +00:00
static const char * const _silly_company_names [ ] = {
2004-08-09 17:04:08 +00:00
" Bloggs Brothers " ,
" Tiny Transport Ltd. " ,
" Express Travel " ,
" Comfy-Coach & Co. " ,
" Crush & Bump Ltd. " ,
" Broken & Late Ltd. " ,
" Sam Speedy & Son " ,
" Supersonic Travel " ,
" Mike's Motors " ,
" Lightning International " ,
" Pannik & Loozit Ltd. " ,
" Inter-City Transport " ,
2005-11-16 11:17:52 +00:00
" Getout & Pushit Ltd. "
2004-08-09 17:04:08 +00:00
} ;
2005-11-16 11:17:52 +00:00
static const char * const _surname_list [ ] = {
2004-08-09 17:04:08 +00:00
" Adams " ,
" Allan " ,
" Baker " ,
" Bigwig " ,
" Black " ,
" Bloggs " ,
" Brown " ,
" Campbell " ,
" Gordon " ,
" Hamilton " ,
" Hawthorn " ,
" Higgins " ,
" Green " ,
" Gribble " ,
" Jones " ,
" McAlpine " ,
" MacDonald " ,
" McIntosh " ,
" Muir " ,
" Murphy " ,
" Nelson " ,
" O'Donnell " ,
" Parker " ,
" Phillips " ,
" Pilkington " ,
" Quigley " ,
" Sharkey " ,
" Thomson " ,
2005-11-16 11:17:52 +00:00
" Watkins "
} ;
static const char * const _silly_surname_list [ ] = {
2004-08-09 17:04:08 +00:00
" Grumpy " ,
" Dozy " ,
" Speedy " ,
" Nosey " ,
" Dribble " ,
" Mushroom " ,
" Cabbage " ,
" Sniffle " ,
" Fishy " ,
" Swindle " ,
" Sneaky " ,
2005-11-16 11:17:52 +00:00
" Nutkins "
2004-08-09 17:04:08 +00:00
} ;
2005-02-06 09:52:06 +00:00
static const char _initial_name_letters [ ] = {
2005-02-06 16:56:04 +00:00
' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' , ' G ' , ' H ' , ' I ' , ' J ' ,
' K ' , ' L ' , ' M ' , ' N ' , ' P ' , ' R ' , ' S ' , ' T ' , ' W ' ,
2004-08-09 17:04:08 +00:00
} ;
2006-10-21 23:31:34 +00:00
static char * GenAndCoName ( char * buff , uint32 arg , const char * last )
2004-08-09 17:04:08 +00:00
{
2005-11-16 11:17:52 +00:00
const char * const * base ;
uint num ;
2004-08-09 17:04:08 +00:00
2007-03-22 03:42:43 +00:00
if ( _opt_ptr - > landscape = = LT_TOYLAND ) {
2005-11-16 11:17:52 +00:00
base = _silly_surname_list ;
num = lengthof ( _silly_surname_list ) ;
} else {
base = _surname_list ;
num = lengthof ( _surname_list ) ;
2004-08-09 17:04:08 +00:00
}
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , base [ num * GB ( arg , 16 , 8 ) > > 8 ] , last ) ;
buff = strecpy ( buff , " & Co. " , last ) ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2006-10-21 23:31:34 +00:00
static char * GenPresidentName ( char * buff , uint32 x , const char * last )
2004-08-09 17:04:08 +00:00
{
2006-10-21 23:31:34 +00:00
char initial [ ] = " ?. " ;
2005-11-16 11:17:52 +00:00
const char * const * base ;
uint num ;
uint i ;
2004-08-09 17:04:08 +00:00
2006-10-21 23:31:34 +00:00
initial [ 0 ] = _initial_name_letters [ sizeof ( _initial_name_letters ) * GB ( x , 0 , 8 ) > > 8 ] ;
buff = strecpy ( buff , initial , last ) ;
2004-08-09 17:04:08 +00:00
2005-07-21 06:31:02 +00:00
i = ( sizeof ( _initial_name_letters ) + 35 ) * GB ( x , 8 , 8 ) > > 8 ;
2004-08-09 17:04:08 +00:00
if ( i < sizeof ( _initial_name_letters ) ) {
2006-10-21 23:31:34 +00:00
initial [ 0 ] = _initial_name_letters [ i ] ;
buff = strecpy ( buff , initial , last ) ;
2004-08-09 17:04:08 +00:00
}
2007-03-22 03:42:43 +00:00
if ( _opt_ptr - > landscape = = LT_TOYLAND ) {
2005-11-16 11:17:52 +00:00
base = _silly_surname_list ;
num = lengthof ( _silly_surname_list ) ;
} else {
base = _surname_list ;
num = lengthof ( _surname_list ) ;
2004-08-09 17:04:08 +00:00
}
2006-10-21 23:31:34 +00:00
buff = strecpy ( buff , base [ num * GB ( x , 16 , 8 ) > > 8 ] , last ) ;
2004-08-09 17:04:08 +00:00
return buff ;
}
2007-06-21 17:25:17 +00:00
static char * GetSpecialPlayerNameString ( char * buff , int ind , const int64 * argv , const char * last )
2004-08-09 17:04:08 +00:00
{
2005-02-06 11:23:41 +00:00
switch ( ind ) {
2005-02-06 16:56:04 +00:00
case 1 : // not used
2006-10-21 23:31:34 +00:00
return strecpy ( buff , _silly_company_names [ GetInt32 ( & argv ) & 0xFFFF ] , last ) ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
case 2 : // used for Foobar & Co company names
2006-10-21 23:31:34 +00:00
return GenAndCoName ( buff , GetInt32 ( & argv ) , last ) ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
case 3 : // President name
2006-10-21 23:31:34 +00:00
return GenPresidentName ( buff , GetInt32 ( & argv ) , last ) ;
2004-08-09 17:04:08 +00:00
2005-02-06 16:56:04 +00:00
case 4 : // song names
2006-10-21 23:31:34 +00:00
return strecpy ( buff , origin_songs_specs [ GetInt32 ( & argv ) - 1 ] . song_name , last ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 01:35:16 +00:00
/* town name? */
2007-11-24 10:38:43 +00:00
if ( IsInsideMM ( ind - 6 , 0 , SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1 ) ) {
2006-10-21 23:31:34 +00:00
buff = GetSpecialTownNameString ( buff , ind - 6 , GetInt32 ( & argv ) , last ) ;
return strecpy ( buff , " Transport " , last ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 01:35:16 +00:00
/* language name? */
2007-11-24 10:38:43 +00:00
if ( IsInsideMM ( ind , ( SPECSTR_LANGUAGE_START - 0x70E4 ) , ( SPECSTR_LANGUAGE_END - 0x70E4 ) + 1 ) ) {
2004-08-09 17:04:08 +00:00
int i = ind - ( SPECSTR_LANGUAGE_START - 0x70E4 ) ;
2005-05-22 07:43:18 +00:00
return strecpy ( buff ,
2006-10-21 23:31:34 +00:00
i = = _dynlang . curr ? _langpack - > own_name : _dynlang . ent [ i ] . name , last ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 01:35:16 +00:00
/* resolution size? */
2007-11-24 10:38:43 +00:00
if ( IsInsideMM ( ind , ( SPECSTR_RESOLUTION_START - 0x70E4 ) , ( SPECSTR_RESOLUTION_END - 0x70E4 ) + 1 ) ) {
2004-08-09 17:04:08 +00:00
int i = ind - ( SPECSTR_RESOLUTION_START - 0x70E4 ) ;
2006-10-21 23:31:34 +00:00
buff + = snprintf (
buff , last - buff + 1 , " %dx%d " , _resolutions [ i ] [ 0 ] , _resolutions [ i ] [ 1 ]
) ;
return buff ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 01:35:16 +00:00
/* screenshot format name? */
2007-11-24 10:38:43 +00:00
if ( IsInsideMM ( ind , ( SPECSTR_SCREENSHOT_START - 0x70E4 ) , ( SPECSTR_SCREENSHOT_END - 0x70E4 ) + 1 ) ) {
2004-08-09 17:04:08 +00:00
int i = ind - ( SPECSTR_SCREENSHOT_START - 0x70E4 ) ;
2006-10-21 23:31:34 +00:00
return strecpy ( buff , GetScreenshotFormatDesc ( i ) , last ) ;
2004-08-09 17:04:08 +00:00
}
assert ( 0 ) ;
return NULL ;
}
2007-04-04 01:35:16 +00:00
/**
* remap a string ID from the old format to the new format
* @ param s StringID that requires remapping
* @ return translated ID */
2004-08-09 17:04:08 +00:00
StringID RemapOldStringID ( StringID s )
{
2005-02-06 16:56:04 +00:00
switch ( s ) {
case 0x0006 : return STR_SV_EMPTY ;
case 0x7000 : return STR_SV_UNNAMED ;
case 0x70E4 : return SPECSTR_PLAYERNAME_ENGLISH ;
case 0x70E9 : return SPECSTR_PLAYERNAME_ENGLISH ;
case 0x8864 : return STR_SV_TRAIN_NAME ;
case 0x902B : return STR_SV_ROADVEH_NAME ;
case 0x9830 : return STR_SV_SHIP_NAME ;
case 0xA02F : return STR_SV_AIRCRAFT_NAME ;
default :
2007-11-24 10:38:43 +00:00
if ( IsInsideMM ( s , 0x300F , 0x3030 ) ) {
2005-02-06 16:56:04 +00:00
return s - 0x300F + STR_SV_STNAME ;
2006-02-01 06:32:03 +00:00
} else {
2005-02-06 16:56:04 +00:00
return s ;
2006-02-01 06:32:03 +00:00
}
2005-02-06 16:56:04 +00:00
}
2004-08-09 17:04:08 +00:00
}
2007-06-13 17:34:05 +00:00
# ifdef ENABLE_NETWORK
2007-06-04 16:07:22 +00:00
extern void SortNetworkLanguages ( ) ;
2007-06-13 17:34:05 +00:00
# else /* ENABLE_NETWORK */
static inline void SortNetworkLanguages ( ) { }
# endif /* ENABLE_NETWORK */
2007-06-04 19:51:31 +00:00
extern void SortTownGeneratorNames ( ) ;
2007-06-04 16:07:22 +00:00
2005-02-06 11:23:41 +00:00
bool ReadLanguagePack ( int lang_index )
{
2004-08-09 17:04:08 +00:00
int tot_count , i ;
size_t len ;
2005-02-06 09:52:06 +00:00
char * * langpack_offs ;
char * s ;
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
LanguagePack * lang_pack = ( LanguagePack * ) ReadFileToMem ( _dynlang . ent [ lang_index ] . file , & len , 200000 ) ;
2004-08-09 17:04:08 +00:00
if ( lang_pack = = NULL ) return false ;
2005-02-06 14:47:56 +00:00
if ( len < sizeof ( LanguagePack ) | |
lang_pack - > ident ! = TO_LE32 ( LANGUAGE_PACK_IDENT ) | |
lang_pack - > version ! = TO_LE32 ( LANGUAGE_PACK_VERSION ) ) {
2004-08-09 17:04:08 +00:00
free ( lang_pack ) ;
return false ;
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
# if defined(TTD_BIG_ENDIAN)
2005-02-06 11:23:41 +00:00
for ( i = 0 ; i ! = 32 ; i + + ) {
2006-02-03 21:51:42 +00:00
lang_pack - > offsets [ i ] = ReadLE16Aligned ( & lang_pack - > offsets [ i ] ) ;
2004-08-09 17:04:08 +00:00
}
# endif
tot_count = 0 ;
2005-02-06 11:23:41 +00:00
for ( i = 0 ; i ! = 32 ; i + + ) {
2005-02-06 14:47:56 +00:00
uint num = lang_pack - > offsets [ i ] ;
2004-08-09 17:04:08 +00:00
_langtab_start [ i ] = tot_count ;
_langtab_num [ i ] = num ;
tot_count + = num ;
}
2007-04-04 01:35:16 +00:00
/* Allocate offsets */
2007-01-11 17:29:39 +00:00
langpack_offs = MallocT < char * > ( tot_count ) ;
2004-08-09 17:04:08 +00:00
2007-04-04 01:35:16 +00:00
/* Fill offsets */
2005-02-06 14:47:56 +00:00
s = lang_pack - > data ;
2005-02-06 11:23:41 +00:00
for ( i = 0 ; i ! = tot_count ; i + + ) {
2005-02-06 09:52:06 +00:00
len = ( byte ) * s ;
2005-02-06 11:23:41 +00:00
* s + + = ' \0 ' ; // zero terminate the string before.
if ( len > = 0xC0 ) len = ( ( len & 0x3F ) < < 8 ) + ( byte ) * s + + ;
2004-08-09 17:04:08 +00:00
langpack_offs [ i ] = s ;
s + = len ;
}
2005-02-06 16:56:04 +00:00
free ( _langpack ) ;
2004-08-09 17:04:08 +00:00
_langpack = lang_pack ;
2005-02-06 16:56:04 +00:00
free ( _langpack_offs ) ;
2004-08-09 17:04:08 +00:00
_langpack_offs = langpack_offs ;
2007-03-20 14:01:21 +00:00
const char * c_file = strrchr ( _dynlang . ent [ lang_index ] . file , PATHSEPCHAR ) + 1 ;
ttd_strlcpy ( _dynlang . curr_file , c_file , lengthof ( _dynlang . curr_file ) ) ;
2004-08-09 17:04:08 +00:00
_dynlang . curr = lang_index ;
2006-04-21 03:00:20 +00:00
SetCurrentGrfLangID ( _langpack - > isocode ) ;
2007-06-04 16:07:22 +00:00
SortNetworkLanguages ( ) ;
2007-06-04 19:51:31 +00:00
SortTownGeneratorNames ( ) ;
2004-08-09 17:04:08 +00:00
return true ;
}
2007-07-27 22:19:19 +00:00
/* Win32 implementation in win32.cpp. */
2007-08-11 15:52:34 +00:00
/* OS X implementation in os/macosx/macos.mm. */
# if !(defined(WIN32) || defined(__APPLE__))
2006-03-25 09:22:10 +00:00
/** Determine the current charset based on the environment
* First check some default values , after this one we passed ourselves
* and if none exist return the value for $ LANG
2007-04-04 01:35:16 +00:00
* @ param param environment variable to check conditionally if default ones are not
2006-03-25 09:22:10 +00:00
* set . Pass NULL if you don ' t want additional checks .
* @ return return string containing current charset , or NULL if not - determinable */
const char * GetCurrentLocale ( const char * param )
{
const char * env ;
env = getenv ( " LANGUAGE " ) ;
if ( env ! = NULL ) return env ;
env = getenv ( " LC_ALL " ) ;
if ( env ! = NULL ) return env ;
if ( param ! = NULL ) {
env = getenv ( param ) ;
if ( env ! = NULL ) return env ;
}
return getenv ( " LANG " ) ;
}
2007-08-11 15:52:34 +00:00
# endif /* !(defined(WIN32) || defined(__APPLE__)) */
2006-03-25 09:22:10 +00:00
2006-08-04 23:45:20 +00:00
static int CDECL LanguageCompareFunc ( const void * a , const void * b )
{
2007-03-17 22:21:05 +00:00
const Language * cmp1 = ( const Language * ) a ;
const Language * cmp2 = ( const Language * ) b ;
return strcmp ( cmp1 - > file , cmp2 - > file ) ;
}
2007-06-04 16:07:22 +00:00
int CDECL StringIDSorter ( const void * a , const void * b )
{
const StringID va = * ( const StringID * ) a ;
const StringID vb = * ( const StringID * ) b ;
char stra [ 512 ] ;
char strb [ 512 ] ;
GetString ( stra , va , lastof ( stra ) ) ;
GetString ( strb , vb , lastof ( strb ) ) ;
return strcmp ( stra , strb ) ;
}
2007-03-17 22:21:05 +00:00
/**
* Checks whether the given language is already found .
* @ param langs languages we ' ve found so fa
* @ param max the length of the language list
* @ param language name of the language to check
* @ return true if and only if a language file with the same name has not been found
*/
static bool UniqueLanguageFile ( const Language * langs , uint max , const char * language )
{
for ( uint i = 0 ; i < max ; i + + ) {
2007-03-20 14:01:21 +00:00
const char * f_name = strrchr ( langs [ i ] . file , PATHSEPCHAR ) + 1 ;
2007-03-17 22:21:05 +00:00
if ( strcmp ( f_name , language ) = = 0 ) return false ; // duplicates
}
return true ;
}
/**
* Reads the language file header and checks compatability .
* @ param file the file to read
* @ param hdr the place to write the header information to
* @ return true if and only if the language file is of a compatible version
*/
static bool GetLanguageFileHeader ( const char * file , LanguagePack * hdr )
{
FILE * f = fopen ( file , " rb " ) ;
if ( f = = NULL ) return false ;
size_t read = fread ( hdr , sizeof ( * hdr ) , 1 , f ) ;
fclose ( f ) ;
return read = = 1 & &
hdr - > ident = = TO_LE32 ( LANGUAGE_PACK_IDENT ) & &
hdr - > version = = TO_LE32 ( LANGUAGE_PACK_VERSION ) ;
2006-08-04 23:45:20 +00:00
}
2007-03-17 22:21:05 +00:00
/**
* Gets a list of languages from the given directory .
* @ param langs the list to write to
* @ param start the initial offset in the list
* @ param max the length of the language list
* @ param path the base directory to search in
* @ return the number of added languages
*/
static int GetLanguageList ( Language * langs , int start , int max , const char * path )
2006-08-04 23:45:20 +00:00
{
2007-03-17 22:21:05 +00:00
int i = start ;
2006-08-04 23:45:20 +00:00
2007-03-17 22:21:05 +00:00
DIR * dir = ttd_opendir ( path ) ;
2006-08-04 23:45:20 +00:00
if ( dir ! = NULL ) {
2007-03-17 22:21:05 +00:00
struct dirent * dirent ;
while ( ( dirent = readdir ( dir ) ) ! = NULL & & i < max ) {
const char * d_name = FS2OTTD ( dirent - > d_name ) ;
const char * extension = strrchr ( d_name , ' . ' ) ;
2006-08-04 23:45:20 +00:00
2007-03-17 22:21:05 +00:00
/* Not a language file */
if ( extension = = NULL | | strcmp ( extension , " .lng " ) ! = 0 ) continue ;
/* Filter any duplicate language-files, first-come first-serve */
if ( ! UniqueLanguageFile ( langs , i , d_name ) ) continue ;
langs [ i ] . file = str_fmt ( " %s%s " , path , d_name ) ;
/* Check whether the file is of the correct version */
LanguagePack hdr ;
if ( ! GetLanguageFileHeader ( langs [ i ] . file , & hdr ) ) {
free ( langs [ i ] . file ) ;
continue ;
2006-08-04 23:45:20 +00:00
}
2007-03-17 22:21:05 +00:00
i + + ;
2006-08-04 23:45:20 +00:00
}
closedir ( dir ) ;
}
2007-03-17 22:21:05 +00:00
return i - start ;
2006-08-04 23:45:20 +00:00
}
2007-03-17 22:21:05 +00:00
/**
* Make a list of the available language packs . put the data in
* _dynlang struct .
*/
2007-03-07 11:47:46 +00:00
void InitializeLanguagePacks ( )
2004-08-09 17:04:08 +00:00
{
2007-06-17 15:48:57 +00:00
Searchpath sp ;
2007-03-17 22:21:05 +00:00
Language files [ MAX_LANG ] ;
2007-06-17 15:48:57 +00:00
uint language_count = 0 ;
FOR_ALL_SEARCHPATHS ( sp ) {
char path [ MAX_PATH ] ;
FioAppendDirectory ( path , lengthof ( path ) , sp , LANG_DIR ) ;
language_count + = GetLanguageList ( files , language_count , lengthof ( files ) , path ) ;
}
2007-03-17 22:21:05 +00:00
if ( language_count = = 0 ) error ( " No available language packs (invalid versions?) " ) ;
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
/* Sort the language names alphabetically */
qsort ( files , language_count , sizeof ( Language ) , LanguageCompareFunc ) ;
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
/* Acquire the locale of the current system */
const char * lang = GetCurrentLocale ( " LC_MESSAGES " ) ;
if ( lang = = NULL ) lang = " en_GB " ;
2004-09-10 19:02:27 +00:00
2007-03-17 22:21:05 +00:00
int chosen_language = - 1 ; ///< Matching the language in the configuartion file or the current locale
int language_fallback = - 1 ; ///< Using pt_PT for pt_BR locale when pt_BR is not available
int en_GB_fallback = 0 ; ///< Fallback when no locale-matching language has been found
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
DynamicLanguages * dl = & _dynlang ;
dl - > num = 0 ;
/* Fill the dynamic languages structures */
for ( uint i = 0 ; i < language_count ; i + + ) {
/* File read the language header */
LanguagePack hdr ;
if ( ! GetLanguageFileHeader ( files [ i ] . file , & hdr ) ) continue ;
dl - > ent [ dl - > num ] . file = files [ i ] . file ;
dl - > ent [ dl - > num ] . name = strdup ( hdr . name ) ;
dl - > dropdown [ dl - > num ] = SPECSTR_LANGUAGE_START + dl - > num ;
/* We are trying to find a default language. The priority is by
* configuration file , local environment and last , if nothing found ,
* english . If def equals - 1 , we have not picked a default language */
2007-03-20 14:01:21 +00:00
const char * lang_file = strrchr ( dl - > ent [ dl - > num ] . file , PATHSEPCHAR ) + 1 ;
if ( strcmp ( lang_file , dl - > curr_file ) = = 0 ) chosen_language = dl - > num ;
2007-03-17 22:21:05 +00:00
if ( chosen_language = = - 1 ) {
if ( strcmp ( hdr . isocode , " en_GB " ) = = 0 ) en_GB_fallback = dl - > num ;
if ( strncmp ( hdr . isocode , lang , 5 ) = = 0 ) chosen_language = dl - > num ;
if ( strncmp ( hdr . isocode , lang , 2 ) = = 0 ) language_fallback = dl - > num ;
}
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
dl - > num + + ;
}
/* Terminate the dropdown list */
dl - > dropdown [ dl - > num ] = INVALID_STRING_ID ;
2004-09-10 19:02:27 +00:00
2007-03-17 22:21:05 +00:00
if ( dl - > num = = 0 ) error ( " Invalid version of language packs " ) ;
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
/* We haven't found the language in the config nor the one in the locale.
* Now we set it to one of the fallback languages */
if ( chosen_language = = - 1 ) {
chosen_language = ( language_fallback ! = - 1 ) ? language_fallback : en_GB_fallback ;
}
2004-08-09 17:04:08 +00:00
2007-03-17 22:21:05 +00:00
if ( ! ReadLanguagePack ( chosen_language ) ) error ( " Can't read language pack '%s' " , dl - > ent [ chosen_language ] . file ) ;
2005-07-15 21:28:26 +00:00
}
2007-12-16 18:38:19 +00:00
/**
* Check whether the currently loaded language pack
* uses characters that the currently loaded font
* does not support . If this is the case an error
* message will be shown in English . The error
* message will not be localized because that would
* mean it might use characters that are not in the
* font , which is the whole reason this check has
* been added .
*/
void CheckForMissingGlyphsInLoadedLanguagePack ( )
{
2008-01-05 22:19:27 +00:00
const Sprite * question_mark = GetGlyph ( FS_NORMAL , ' ? ' ) ;
2007-12-16 18:38:19 +00:00
for ( uint i = 0 ; i ! = 32 ; i + + ) {
for ( uint j = 0 ; j < _langtab_num [ i ] ; j + + ) {
const char * string = _langpack_offs [ _langtab_start [ i ] + j ] ;
WChar c ;
while ( ( c = Utf8Consume ( & string ) ) ! = ' \0 ' ) {
if ( c = = SCC_SETX ) {
/*
* SetX is , together with SetXY as special character that
* uses the next ( two ) characters as data points . We have
* to skip those , otherwise the UTF8 reading will go
* haywire .
*/
string + + ;
} else if ( c = = SCC_SETXY ) {
string + = 2 ;
2008-01-05 22:19:27 +00:00
} else if ( IsPrintable ( c ) & & c ! = ' ? ' & & GetGlyph ( FS_NORMAL , c ) = = question_mark ) {
2007-12-16 18:38:19 +00:00
/*
* The character is printable , but not in the normal font .
* This is the case we were testing for . In this case we
* have to show the error . As we do not want the string to
* be translated by the translators , we ' force ' it into the
* binary and ' load ' it via a BindCString . To do this
* properly we have to set the color of the string ,
* otherwise we end up with a lot of artefacts . The color
* ' character ' might change in the future , so for safety
* we just Utf8 Encode it into the string , which takes
* exactly three characters , so it replaces the " XXX " with
* the color marker .
*/
2008-01-05 22:34:04 +00:00
static char * err_str = strdup ( " XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this. " ) ;
2007-12-16 18:38:19 +00:00
Utf8Encode ( err_str , SCC_YELLOW ) ;
StringID err_msg = BindCString ( err_str ) ;
ShowErrorMessage ( INVALID_STRING_ID , err_msg , 0 , 0 ) ;
return ;
}
}
}
}
}