mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r2597) Feature: [string system] Support cases.
- Cases are used to change a string, such as Coal Mine, depending on the surrounding context. - Cases are defined like "STR_4802_COAL_MINE.ack :Coala Mina" - All cases need to be listed on the top of the file like this "##case ack" - When using the string, type {STRING.ack} to choose the "ack" version of Coal mine. - Also combined the strgen arrays into a struct, and fixed a bug with SetXY.
This commit is contained in:
parent
10bc66eb42
commit
b72e1fb67d
452
strgen/strgen.c
452
strgen/strgen.c
|
@ -37,16 +37,36 @@ typedef struct CmdStruct {
|
||||||
ParseCmdProc proc;
|
ParseCmdProc proc;
|
||||||
long value;
|
long value;
|
||||||
int8 consumes;
|
int8 consumes;
|
||||||
bool dont_count;
|
byte flags;
|
||||||
} CmdStruct;
|
} CmdStruct;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
C_DONTCOUNT = 1,
|
||||||
|
C_CASE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct Case {
|
||||||
|
int caseidx;
|
||||||
|
char *string;
|
||||||
|
struct Case *next;
|
||||||
|
} Case;
|
||||||
|
|
||||||
static int _cur_line;
|
static int _cur_line;
|
||||||
static int _errors, _warnings;
|
static int _errors, _warnings;
|
||||||
|
|
||||||
static char *_strname[65536]; // Name of the string, NULL if not exists
|
typedef struct LangString {
|
||||||
static char *_master[65536]; // English text
|
char *name; // Name of the string
|
||||||
static char *_translated[65536]; // Translated text
|
char *english; // English text
|
||||||
static uint16 _hash_next[65536]; // next hash entry
|
char *translated; // Translated text
|
||||||
|
uint16 hash_next; // next hash entry
|
||||||
|
uint16 index;
|
||||||
|
Case *english_case; // cases for english
|
||||||
|
Case *translated_case; // cases for foreign
|
||||||
|
} LangString;
|
||||||
|
|
||||||
|
static LangString *_strings[65536];
|
||||||
|
|
||||||
|
|
||||||
#define HASH_SIZE 32767
|
#define HASH_SIZE 32767
|
||||||
static uint16 _hash_head[HASH_SIZE];
|
static uint16 _hash_head[HASH_SIZE];
|
||||||
|
@ -58,15 +78,35 @@ static int _next_string_id;
|
||||||
static uint32 _hash;
|
static uint32 _hash;
|
||||||
static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
|
static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
|
||||||
static byte _lang_pluralform;
|
static byte _lang_pluralform;
|
||||||
|
#define MAX_NUM_GENDER 8
|
||||||
static char _genders[8][8];
|
static char _genders[MAX_NUM_GENDER][8];
|
||||||
static int _numgenders;
|
static int _numgenders;
|
||||||
|
|
||||||
|
// contains the name of all cases.
|
||||||
|
#define MAX_NUM_CASES 50
|
||||||
|
static char _cases[MAX_NUM_CASES][16];
|
||||||
|
static int _numcases;
|
||||||
|
|
||||||
// for each plural value, this is the number of plural forms.
|
// for each plural value, this is the number of plural forms.
|
||||||
static const byte _plural_form_counts[] = { 2,1,2,3,3,3,3,3,4 };
|
static const byte _plural_form_counts[] = { 2,1,2,3,3,3,3,3,4 };
|
||||||
|
|
||||||
static const char *_cur_ident;
|
static const char *_cur_ident;
|
||||||
|
|
||||||
|
typedef struct CmdPair {
|
||||||
|
const CmdStruct *a;
|
||||||
|
char *v;
|
||||||
|
} CmdPair;
|
||||||
|
|
||||||
|
typedef struct ParsedCommandStruct {
|
||||||
|
int np;
|
||||||
|
CmdPair pairs[32];
|
||||||
|
const CmdStruct *cmd[32]; // ordered by param #
|
||||||
|
} ParsedCommandStruct;
|
||||||
|
|
||||||
|
// Used when generating some advanced commands.
|
||||||
|
static ParsedCommandStruct _cur_pcs;
|
||||||
|
static int _cur_argidx;
|
||||||
|
|
||||||
static uint HashStr(const char *s)
|
static uint HashStr(const char *s)
|
||||||
{
|
{
|
||||||
uint hash = 0;
|
uint hash = 0;
|
||||||
|
@ -75,21 +115,22 @@ static uint HashStr(const char *s)
|
||||||
return hash % HASH_SIZE;
|
return hash % HASH_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HashAdd(const char *s, int value)
|
static void HashAdd(const char *s, LangString *ls)
|
||||||
{
|
{
|
||||||
uint hash = HashStr(s);
|
uint hash = HashStr(s);
|
||||||
_hash_next[value] = _hash_head[hash];
|
ls->hash_next = _hash_head[hash];
|
||||||
_hash_head[hash] = value + 1;
|
_hash_head[hash] = ls->index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int HashFind(const char *s)
|
static LangString *HashFind(const char *s)
|
||||||
{
|
{
|
||||||
int idx = _hash_head[HashStr(s)];
|
int idx = _hash_head[HashStr(s)];
|
||||||
while (--idx >= 0) {
|
while (--idx >= 0) {
|
||||||
if (!strcmp(_strname[idx], s)) return idx;
|
LangString *ls = _strings[idx];
|
||||||
idx = _hash_next[idx];
|
if (!strcmp(ls->name, s)) return ls;
|
||||||
|
idx = ls->hash_next;
|
||||||
}
|
}
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +203,6 @@ static void EmitEscapedByte(char *buf, int value)
|
||||||
PutByte((byte)value);
|
PutByte((byte)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void EmitSetX(char *buf, int value)
|
static void EmitSetX(char *buf, int value)
|
||||||
{
|
{
|
||||||
char *err;
|
char *err;
|
||||||
|
@ -184,7 +224,7 @@ static void EmitSetXY(char *buf, int value)
|
||||||
y = strtol(err+1, &err, 0);
|
y = strtol(err+1, &err, 0);
|
||||||
if (*err != 0) Fatal("SetXY param invalid");
|
if (*err != 0) Fatal("SetXY param invalid");
|
||||||
|
|
||||||
PutByte(0x1F);
|
PutByte(2);
|
||||||
PutByte((byte)x);
|
PutByte((byte)x);
|
||||||
PutByte((byte)y);
|
PutByte((byte)y);
|
||||||
}
|
}
|
||||||
|
@ -195,15 +235,20 @@ static void EmitSetXY(char *buf, int value)
|
||||||
// This is encoded like
|
// This is encoded like
|
||||||
// CommandByte <ARG#> <NUM> {Length of each string} {each string}
|
// CommandByte <ARG#> <NUM> {Length of each string} {each string}
|
||||||
|
|
||||||
bool ParseRelNum(char **buf, int *value, bool *relative)
|
bool ParseRelNum(char **buf, int *value)
|
||||||
{
|
{
|
||||||
char *s = *buf, *end;
|
char *s = *buf, *end;
|
||||||
bool rel = false;
|
bool rel = false;
|
||||||
|
int v;
|
||||||
|
|
||||||
while (*s == ' ' || *s == '\t') s++;
|
while (*s == ' ' || *s == '\t') s++;
|
||||||
if (*s == '+') { rel = true; s++; }
|
if (*s == '+') { rel = true; s++; }
|
||||||
*value = strtol(s, &end, 0);
|
v = strtol(s, &end, 0);
|
||||||
if (end == s) return false;
|
if (end == s) return false;
|
||||||
*relative = rel | (*value < 0);
|
if (rel || (v < 0))
|
||||||
|
*value += v;
|
||||||
|
else
|
||||||
|
*value = v;
|
||||||
*buf = end;
|
*buf = end;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +291,7 @@ char *ParseWord(char **buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
static int TranslateArgumentIdx(int arg, bool relative);
|
static int TranslateArgumentIdx(int arg);
|
||||||
|
|
||||||
static void EmitWordList(char **words, int nw)
|
static void EmitWordList(char **words, int nw)
|
||||||
{
|
{
|
||||||
|
@ -263,13 +308,13 @@ static void EmitWordList(char **words, int nw)
|
||||||
|
|
||||||
static void EmitPlural(char *buf, int value)
|
static void EmitPlural(char *buf, int value)
|
||||||
{
|
{
|
||||||
int v;
|
int argidx = _cur_argidx;
|
||||||
bool relative;
|
|
||||||
char *words[5];
|
char *words[5];
|
||||||
int nw = 0;
|
int nw = 0;
|
||||||
|
|
||||||
// Parse out the number, if one exists.
|
// Parse out the number, if one exists. Otherwise default to prev arg.
|
||||||
if (!ParseRelNum(&buf, &v, &relative)) { v = -1; relative = true; }
|
if (!ParseRelNum(&buf, &argidx))
|
||||||
|
argidx--;
|
||||||
|
|
||||||
// Parse each string
|
// Parse each string
|
||||||
for(nw=0; nw<5; nw++) {
|
for(nw=0; nw<5; nw++) {
|
||||||
|
@ -286,15 +331,14 @@ static void EmitPlural(char *buf, int value)
|
||||||
_plural_form_counts[_lang_pluralform], nw);
|
_plural_form_counts[_lang_pluralform], nw);
|
||||||
|
|
||||||
PutByte(0x7D);
|
PutByte(0x7D);
|
||||||
PutByte(TranslateArgumentIdx(v, relative));
|
PutByte(TranslateArgumentIdx(argidx));
|
||||||
EmitWordList(words, nw);
|
EmitWordList(words, nw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void EmitGender(char *buf, int value)
|
static void EmitGender(char *buf, int value)
|
||||||
{
|
{
|
||||||
int v;
|
int argidx = _cur_argidx;
|
||||||
bool relative;
|
|
||||||
char *words[8];
|
char *words[8];
|
||||||
int nw;
|
int nw;
|
||||||
|
|
||||||
|
@ -314,7 +358,8 @@ static void EmitGender(char *buf, int value)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// This is a {G 0 foo bar two} command.
|
// This is a {G 0 foo bar two} command.
|
||||||
if (!ParseRelNum(&buf, &v, &relative)) { v = 0; relative = true; }
|
// If no relative number exists, default to +0
|
||||||
|
if (!ParseRelNum(&buf, &argidx)) {}
|
||||||
|
|
||||||
for(nw=0; nw<8; nw++) {
|
for(nw=0; nw<8; nw++) {
|
||||||
words[nw] = ParseWord(&buf);
|
words[nw] = ParseWord(&buf);
|
||||||
|
@ -324,7 +369,7 @@ static void EmitGender(char *buf, int value)
|
||||||
if (nw != _numgenders) Fatal("Bad # of arguments for gender command");
|
if (nw != _numgenders) Fatal("Bad # of arguments for gender command");
|
||||||
PutByte(0x85);
|
PutByte(0x85);
|
||||||
PutByte(13);
|
PutByte(13);
|
||||||
PutByte(TranslateArgumentIdx(v, relative));
|
PutByte(TranslateArgumentIdx(argidx));
|
||||||
EmitWordList(words, nw);
|
EmitWordList(words, nw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,7 +385,7 @@ static const CmdStruct _cmd_structs[] = {
|
||||||
{"BIGFONT", EmitSingleByte, 9, 0},
|
{"BIGFONT", EmitSingleByte, 9, 0},
|
||||||
|
|
||||||
// New line
|
// New line
|
||||||
{"", EmitSingleByte, 10, 0, true},
|
{"", EmitSingleByte, 10, 0, C_DONTCOUNT},
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
{"BLUE", EmitSingleByte, 15, 0},
|
{"BLUE", EmitSingleByte, 15, 0},
|
||||||
|
@ -385,18 +430,20 @@ static const CmdStruct _cmd_structs[] = {
|
||||||
// The first string includes the second string.
|
// The first string includes the second string.
|
||||||
|
|
||||||
|
|
||||||
{"STRING1", EmitEscapedByte, 5, 1}, // included string that consumes ONE argument
|
{"STRING1", EmitEscapedByte, 5, 1, C_CASE}, // included string that consumes ONE argument
|
||||||
{"STRING2", EmitEscapedByte, 6, 2}, // included string that consumes TWO arguments
|
{"STRING2", EmitEscapedByte, 6, 2, C_CASE}, // included string that consumes TWO arguments
|
||||||
{"STRING3", EmitEscapedByte, 7, 3}, // included string that consumes THREE arguments
|
{"STRING3", EmitEscapedByte, 7, 3, C_CASE}, // included string that consumes THREE arguments
|
||||||
{"STRING4", EmitEscapedByte, 8, 4}, // included string that consumes FOUR arguments
|
{"STRING4", EmitEscapedByte, 8, 4, C_CASE}, // included string that consumes FOUR arguments
|
||||||
{"STRING5", EmitEscapedByte, 9, 5}, // included string that consumes FIVE arguments
|
{"STRING5", EmitEscapedByte, 9, 5, C_CASE}, // included string that consumes FIVE arguments
|
||||||
|
|
||||||
{"STATIONFEATURES", EmitEscapedByte, 10, 1}, // station features string, icons of the features
|
{"STATIONFEATURES", EmitEscapedByte, 10, 1}, // station features string, icons of the features
|
||||||
{"INDUSTRY", EmitEscapedByte, 11, 1}, // industry, takes an industry #
|
{"INDUSTRY", EmitEscapedByte, 11, 1}, // industry, takes an industry #
|
||||||
{"VOLUME", EmitEscapedByte, 12, 1},
|
{"VOLUME", EmitEscapedByte, 12, 1},
|
||||||
|
{"DATE_TINY", EmitEscapedByte, 14, 1},
|
||||||
|
{"CARGO", EmitEscapedByte, 15, 2},
|
||||||
|
|
||||||
{"P", EmitPlural, 0, 0, true}, // plural specifier
|
{"P", EmitPlural, 0, 0, C_DONTCOUNT}, // plural specifier
|
||||||
{"G", EmitGender, 0, 0, true}, // gender specifier
|
{"G", EmitGender, 0, 0, C_DONTCOUNT}, // gender specifier
|
||||||
|
|
||||||
{"DATE_LONG", EmitSingleByte, 0x82, 1},
|
{"DATE_LONG", EmitSingleByte, 0x82, 1},
|
||||||
{"DATE_SHORT", EmitSingleByte, 0x83, 1},
|
{"DATE_SHORT", EmitSingleByte, 0x83, 1},
|
||||||
|
@ -405,14 +452,15 @@ static const CmdStruct _cmd_structs[] = {
|
||||||
|
|
||||||
{"SKIP", EmitSingleByte, 0x86, 1},
|
{"SKIP", EmitSingleByte, 0x86, 1},
|
||||||
|
|
||||||
{"STRING", EmitSingleByte, 0x88, 1},
|
{"STRING", EmitSingleByte, 0x88, 1, C_CASE},
|
||||||
|
|
||||||
{"CARGO", EmitSingleByte, 0x99, 2},
|
{"WAYPOINT", EmitSingleByte, 0x99, 1}, // waypoint name
|
||||||
{"STATION", EmitSingleByte, 0x9A, 1},
|
{"STATION", EmitSingleByte, 0x9A, 1},
|
||||||
{"TOWN", EmitSingleByte, 0x9B, 1},
|
{"TOWN", EmitSingleByte, 0x9B, 1},
|
||||||
{"CURRENCY64", EmitSingleByte, 0x9C, 2},
|
{"CURRENCY64", EmitSingleByte, 0x9C, 2},
|
||||||
{"WAYPOINT", EmitSingleByte, 0x9D, 1}, // waypoint name
|
// 0x9D is used for the pseudo command SETCASE
|
||||||
{"DATE_TINY", EmitSingleByte, 0x9E, 1},
|
// 0x9E is used for case switching
|
||||||
|
|
||||||
// 0x9E=158 is the LAST special character we may use.
|
// 0x9E=158 is the LAST special character we may use.
|
||||||
|
|
||||||
{"UPARROW", EmitSingleByte, 0xA0, 0},
|
{"UPARROW", EmitSingleByte, 0xA0, 0},
|
||||||
|
@ -447,17 +495,27 @@ static const CmdStruct *FindCmd(const char *s, int len)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ResolveCaseName(const char *str, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0; i<MAX_NUM_CASES; i++)
|
||||||
|
if (!memcmp(_cases[i], str, len) && _cases[i][len] == 0)
|
||||||
|
return i + 1;
|
||||||
|
Fatal("Invalid case-name '%s'", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// returns NULL on eof
|
// returns NULL on eof
|
||||||
// else returns command struct
|
// else returns command struct
|
||||||
static const CmdStruct *ParseCommandString(char **str, char *param, int *argno, bool show_err)
|
static const CmdStruct *ParseCommandString(const char **str, char *param, int *argno, int *casei)
|
||||||
{
|
{
|
||||||
char *s = *str, *start;
|
const char *s = *str, *start;
|
||||||
const CmdStruct *cmd;
|
const CmdStruct *cmd;
|
||||||
int plen = 0;
|
int plen = 0;
|
||||||
byte c;
|
byte c;
|
||||||
|
|
||||||
*argno = -1;
|
*argno = -1;
|
||||||
|
*casei = -1;
|
||||||
|
|
||||||
// Scan to the next command, exit if there's no next command.
|
// Scan to the next command, exit if there's no next command.
|
||||||
for(; *s != '{'; s++) {
|
for(; *s != '{'; s++) {
|
||||||
|
@ -477,15 +535,9 @@ static const CmdStruct *ParseCommandString(char **str, char *param, int *argno,
|
||||||
|
|
||||||
// parse command name
|
// parse command name
|
||||||
start = s;
|
start = s;
|
||||||
for(;;) {
|
do {
|
||||||
c = *s++;
|
c = *s++;
|
||||||
if (c == '}' || c == ' ' || c == '=')
|
} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
|
||||||
break;
|
|
||||||
if (c == '\0') {
|
|
||||||
Error("Missing } from command '%s'", start);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = FindCmd(start, s - start - 1);
|
cmd = FindCmd(start, s - start - 1);
|
||||||
if (cmd == NULL) {
|
if (cmd == NULL) {
|
||||||
|
@ -493,6 +545,22 @@ static const CmdStruct *ParseCommandString(char **str, char *param, int *argno,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c == '.') {
|
||||||
|
const char *casep = s;
|
||||||
|
|
||||||
|
if (!(cmd->flags & C_CASE))
|
||||||
|
Fatal("Command '%s' can't have a case", cmd->cmd);
|
||||||
|
|
||||||
|
do c = *s++; while (c != '}' && c != ' ' && c != '\0');
|
||||||
|
*casei = ResolveCaseName(casep, s-casep-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\0') {
|
||||||
|
Error("Missing } from command '%s'", start);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (c != '}') {
|
if (c != '}') {
|
||||||
if (c == '=') s--;
|
if (c == '=') s--;
|
||||||
// copy params
|
// copy params
|
||||||
|
@ -533,42 +601,40 @@ static void HandlePragma(char *str)
|
||||||
Fatal("Invalid pluralform %d", _lang_pluralform);
|
Fatal("Invalid pluralform %d", _lang_pluralform);
|
||||||
} else if (!memcmp(str, "gender ", 7)) {
|
} else if (!memcmp(str, "gender ", 7)) {
|
||||||
char *buf = str + 7, *s;
|
char *buf = str + 7, *s;
|
||||||
int i;
|
for(;;) {
|
||||||
for(i=0; i<8; i++) {
|
|
||||||
s = ParseWord(&buf);
|
s = ParseWord(&buf);
|
||||||
if (!s) break;
|
if (!s) break;
|
||||||
ttd_strlcpy(_genders[i], s, sizeof(_genders[i]));
|
if (_numgenders >= MAX_NUM_GENDER) Fatal("Too many genders, max %d", MAX_NUM_GENDER);
|
||||||
|
ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
|
||||||
_numgenders++;
|
_numgenders++;
|
||||||
}
|
}
|
||||||
|
} else if (!memcmp(str, "case ", 5)) {
|
||||||
|
char *buf = str + 5, *s;
|
||||||
|
for(;;) {
|
||||||
|
s = ParseWord(&buf);
|
||||||
|
if (!s) break;
|
||||||
|
if (_numcases >= MAX_NUM_CASES) Fatal("Too many cases, max %d", MAX_NUM_CASES);
|
||||||
|
ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
|
||||||
|
_numcases++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Fatal("unknown pragma '%s'", str);
|
Fatal("unknown pragma '%s'", str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct CmdPair {
|
|
||||||
const CmdStruct *a;
|
|
||||||
char *v;
|
|
||||||
} CmdPair;
|
|
||||||
|
|
||||||
typedef struct ParsedCommandStruct {
|
|
||||||
int np;
|
|
||||||
CmdPair pairs[32];
|
|
||||||
const CmdStruct *cmd[32]; // ordered by param #
|
|
||||||
} ParsedCommandStruct;
|
|
||||||
|
|
||||||
|
|
||||||
static void ExtractCommandString(ParsedCommandStruct *p, char *s, bool warnings)
|
static void ExtractCommandString(ParsedCommandStruct *p, char *s, bool warnings)
|
||||||
{
|
{
|
||||||
const CmdStruct *ar;
|
const CmdStruct *ar;
|
||||||
char param[100];
|
char param[100];
|
||||||
int argno;
|
int argno;
|
||||||
int argidx = 0;
|
int argidx = 0;
|
||||||
|
int casei;
|
||||||
|
|
||||||
memset(p, 0, sizeof(*p));
|
memset(p, 0, sizeof(*p));
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
// read until next command from a.
|
// read until next command from a.
|
||||||
ar = ParseCommandString(&s, param, &argno, warnings);
|
ar = ParseCommandString(&s, param, &argno, &casei);
|
||||||
if (ar == NULL)
|
if (ar == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -582,7 +648,7 @@ static void ExtractCommandString(ParsedCommandStruct *p, char *s, bool warnings)
|
||||||
if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) Fatal("duplicate param idx %d", argidx);
|
if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) Fatal("duplicate param idx %d", argidx);
|
||||||
|
|
||||||
p->cmd[argidx++] = ar;
|
p->cmd[argidx++] = ar;
|
||||||
} else if (!ar->dont_count) { // Ignore some of them
|
} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
|
||||||
if (p->np >= lengthof(p->pairs)) Fatal("too many commands in string, max %d", lengthof(p->pairs));
|
if (p->np >= lengthof(p->pairs)) Fatal("too many commands in string, max %d", lengthof(p->pairs));
|
||||||
p->pairs[p->np].a = ar;
|
p->pairs[p->np].a = ar;
|
||||||
p->pairs[p->np].v = param[0]?strdup(param):"";
|
p->pairs[p->np].v = param[0]?strdup(param):"";
|
||||||
|
@ -659,11 +725,11 @@ static bool CheckCommandsMatch(char *a, char *b, const char *name)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void HandleString(char *str, bool master)
|
static void HandleString(char *str, bool master)
|
||||||
{
|
{
|
||||||
char *s,*t;
|
char *s,*t;
|
||||||
int ent;
|
LangString *ent;
|
||||||
|
char *casep;
|
||||||
|
|
||||||
if (*str == '#') {
|
if (*str == '#') {
|
||||||
if (str[1] == '#' && str[2] != '#')
|
if (str[1] == '#' && str[2] != '#')
|
||||||
|
@ -687,45 +753,78 @@ static void HandleString(char *str, bool master)
|
||||||
*t = 0;
|
*t = 0;
|
||||||
s++;
|
s++;
|
||||||
|
|
||||||
|
// Check if the string has a case..
|
||||||
|
// The syntax for cases is IDENTNAME.case
|
||||||
|
casep = strchr(str, '.');
|
||||||
|
if (casep) *casep++ = 0;
|
||||||
|
|
||||||
// Check if this string already exists..
|
// Check if this string already exists..
|
||||||
ent = HashFind(str);
|
ent = HashFind(str);
|
||||||
|
|
||||||
if (master) {
|
if (master) {
|
||||||
if (ent != -1) {
|
if (ent != NULL && !casep) {
|
||||||
Error("String name '%s' is used multiple times", str);
|
Error("String name '%s' is used multiple times", str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ent = _next_string_id++;
|
if (ent == NULL && casep) {
|
||||||
if (_strname[ent]) {
|
Error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
|
||||||
Error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strname[ent]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_strname[ent] = strdup(str);
|
|
||||||
_master[ent] = strdup(s);
|
|
||||||
|
|
||||||
// add to hash table
|
if (ent == NULL) {
|
||||||
HashAdd(str, ent);
|
if (_strings[_next_string_id]) {
|
||||||
|
Error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a new LangString
|
||||||
|
ent = calloc(sizeof(LangString), 1);
|
||||||
|
_strings[_next_string_id] = ent;
|
||||||
|
ent->index = _next_string_id++;
|
||||||
|
ent->name = strdup(str);
|
||||||
|
|
||||||
|
HashAdd(str, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (casep) {
|
||||||
|
Case *c = malloc(sizeof(Case));
|
||||||
|
c->caseidx = ResolveCaseName(casep, strlen(casep));
|
||||||
|
c->string = strdup(s);
|
||||||
|
c->next = ent->english_case;
|
||||||
|
ent->english_case = c;
|
||||||
|
} else {
|
||||||
|
ent->english = strdup(s);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (ent == -1) {
|
if (ent == NULL) {
|
||||||
Warning("String name '%s' does not exist in master file", str);
|
Warning("String name '%s' does not exist in master file", str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_translated[ent]) {
|
if (ent->translated && !casep) {
|
||||||
Error("String name '%s' is used multiple times", str);
|
Error("String name '%s' is used multiple times", str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s[0] == ':' && s[1] == '\0') {
|
if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
|
||||||
// Special syntax :: means we should just inherit the master string
|
// Special syntax :: means we should just inherit the master string
|
||||||
_translated[ent] = strdup(_master[ent]);
|
ent->translated = strdup(ent->english);
|
||||||
} else {
|
} else {
|
||||||
// check that the commands match
|
// make sure that the commands match
|
||||||
if (!CheckCommandsMatch(s, _master[ent], str)) {
|
if (!CheckCommandsMatch(s, ent->english, str))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (casep) {
|
||||||
|
Case *c = malloc(sizeof(Case));
|
||||||
|
c->caseidx = ResolveCaseName(casep, strlen(casep));
|
||||||
|
c->string = strdup(s);
|
||||||
|
c->next = ent->translated_case;
|
||||||
|
ent->translated_case = c;
|
||||||
|
} else {
|
||||||
|
ent->translated = strdup(s);
|
||||||
}
|
}
|
||||||
_translated[ent] = strdup(s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -744,6 +843,12 @@ static void ParseFile(const char *file, bool english)
|
||||||
FILE *in;
|
FILE *in;
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
|
|
||||||
|
// For each new file we parse, reset the genders.
|
||||||
|
_numgenders = 0;
|
||||||
|
// TODO:!! We can't reset the cases. In case the translated strings
|
||||||
|
// derive some strings from english....
|
||||||
|
|
||||||
|
|
||||||
in = fopen(file, "r");
|
in = fopen(file, "r");
|
||||||
if (in == NULL) { Fatal("Cannot open file '%s'", file); }
|
if (in == NULL) { Fatal("Cannot open file '%s'", file); }
|
||||||
_cur_line = 1;
|
_cur_line = 1;
|
||||||
|
@ -770,20 +875,26 @@ static uint32 MyHashStr(uint32 hash, const char *s)
|
||||||
static void MakeHashOfStrings()
|
static void MakeHashOfStrings()
|
||||||
{
|
{
|
||||||
uint32 hash = 0;
|
uint32 hash = 0;
|
||||||
|
LangString *ls;
|
||||||
char *s;
|
char *s;
|
||||||
const CmdStruct *cs;
|
const CmdStruct *cs;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int i;
|
int i;
|
||||||
int argno;
|
int argno;
|
||||||
|
int casei;
|
||||||
|
|
||||||
for(i = 0; i != 65536; i++) {
|
for(i = 0; i != 65536; i++) {
|
||||||
if ((s=_strname[i]) != NULL) {
|
if ((ls=_strings[i]) != NULL) {
|
||||||
|
s = ls->name;
|
||||||
hash ^= i * 0x717239;
|
hash ^= i * 0x717239;
|
||||||
if (hash & 1) hash = (hash>>1) ^ 0xDEADBEEF; else hash >>= 1;
|
if (hash & 1) hash = (hash>>1) ^ 0xDEADBEEF; else hash >>= 1;
|
||||||
hash = MyHashStr(hash, s + 1);
|
hash = MyHashStr(hash, s + 1);
|
||||||
|
|
||||||
s = _master[i];
|
s = ls->english;
|
||||||
while ((cs = ParseCommandString(&s, buf, &argno, false)) != NULL) {
|
while ((cs = ParseCommandString(&s, buf, &argno, &casei)) != NULL) {
|
||||||
|
if (cs->flags & C_DONTCOUNT)
|
||||||
|
continue;
|
||||||
|
|
||||||
hash ^= (cs - _cmd_structs) * 0x1234567;
|
hash ^= (cs - _cmd_structs) * 0x1234567;
|
||||||
if (hash & 1) hash = (hash>>1) ^ 0xF00BAA4; else hash >>= 1;
|
if (hash & 1) hash = (hash>>1) ^ 0xF00BAA4; else hash >>= 1;
|
||||||
}
|
}
|
||||||
|
@ -798,24 +909,13 @@ static int CountInUse(int grp)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for(i = 0x800; --i >= 0;) {
|
for(i = 0x800; --i >= 0;) {
|
||||||
if (_strname[(grp<<11)+i] != NULL)
|
if (_strings[(grp<<11)+i] != NULL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return i + 1;
|
return i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void WriteLength(FILE *f, uint length)
|
|
||||||
{
|
|
||||||
if (length < 0xC0) {
|
|
||||||
fputc(length, f);
|
|
||||||
} else if (length < 0x4000) {
|
|
||||||
fputc((length >> 8) | 0xC0, f);
|
|
||||||
fputc(length & 0xFF, f);
|
|
||||||
} else {
|
|
||||||
Fatal("string too long");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool CompareFiles(const char *n1, const char *n2)
|
bool CompareFiles(const char *n1, const char *n2)
|
||||||
|
@ -863,13 +963,13 @@ static void WriteStringsH(const char *filename)
|
||||||
lastgrp = 0;
|
lastgrp = 0;
|
||||||
|
|
||||||
for(i = 0; i != 65536; i++) {
|
for(i = 0; i != 65536; i++) {
|
||||||
if (_strname[i]) {
|
if (_strings[i]) {
|
||||||
if (lastgrp != (i >> 11)) {
|
if (lastgrp != (i >> 11)) {
|
||||||
lastgrp = (i >> 11);
|
lastgrp = (i >> 11);
|
||||||
fprintf(out, "};\n\nenum {");
|
fprintf(out, "};\n\nenum {");
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(out, next == i ? "%s,\n" : "\n%s = 0x%X,\n", _strname[i], i);
|
fprintf(out, next == i ? "%s,\n" : "\n%s = 0x%X,\n", _strings[i]->name, i);
|
||||||
next = i + 1;
|
next = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -897,16 +997,10 @@ static void WriteStringsH(const char *filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ParsedCommandStruct _cur_pcs;
|
static int TranslateArgumentIdx(int argidx)
|
||||||
static int _cur_argidx;
|
|
||||||
|
|
||||||
static int TranslateArgumentIdx(int argidx, bool relative)
|
|
||||||
{
|
{
|
||||||
int i, sum;
|
int i, sum;
|
||||||
|
|
||||||
if (relative)
|
|
||||||
argidx += _cur_argidx;
|
|
||||||
|
|
||||||
if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
|
if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
|
||||||
Fatal("invalid argidx %d", argidx);
|
Fatal("invalid argidx %d", argidx);
|
||||||
|
|
||||||
|
@ -921,7 +1015,61 @@ static int TranslateArgumentIdx(int argidx, bool relative)
|
||||||
static void PutArgidxCommand(void)
|
static void PutArgidxCommand(void)
|
||||||
{
|
{
|
||||||
PutByte(0x7C);
|
PutByte(0x7C);
|
||||||
PutByte(TranslateArgumentIdx(0, true));
|
PutByte(TranslateArgumentIdx(_cur_argidx));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void PutCommandString(const char *str)
|
||||||
|
{
|
||||||
|
const CmdStruct *cs;
|
||||||
|
char param[256];
|
||||||
|
int argno;
|
||||||
|
int casei;
|
||||||
|
|
||||||
|
_cur_argidx = 0;
|
||||||
|
|
||||||
|
while (*str != '\0') {
|
||||||
|
// Process characters as they are until we encounter a {
|
||||||
|
if (*str != '{') {
|
||||||
|
PutByte(*str++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cs = ParseCommandString(&str, param, &argno, &casei);
|
||||||
|
if (cs == NULL) break;
|
||||||
|
|
||||||
|
if (casei != -1) {
|
||||||
|
PutByte(0x9D); // {SETCASE}
|
||||||
|
PutByte(casei);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For params that consume values, we need to handle the argindex properly
|
||||||
|
if (cs->consumes) {
|
||||||
|
// Check if we need to output a move-param command
|
||||||
|
if (argno!=-1 && argno != _cur_argidx) {
|
||||||
|
_cur_argidx = argno;
|
||||||
|
PutArgidxCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the one from the master string... it's always accurate.
|
||||||
|
cs = _cur_pcs.cmd[_cur_argidx++];
|
||||||
|
if (!cs)
|
||||||
|
Fatal("%s: No argument exists at posision %d", _cur_ident, _cur_argidx-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cs->proc(param, cs->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteLength(FILE *f, uint length)
|
||||||
|
{
|
||||||
|
if (length < 0xC0) {
|
||||||
|
fputc(length, f);
|
||||||
|
} else if (length < 0x4000) {
|
||||||
|
fputc((length >> 8) | 0xC0, f);
|
||||||
|
fputc(length & 0xFF, f);
|
||||||
|
} else {
|
||||||
|
Fatal("string too long");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -931,9 +1079,6 @@ static void WriteLangfile(const char *filename, int show_todo)
|
||||||
int in_use[32];
|
int in_use[32];
|
||||||
LanguagePackHeader hdr;
|
LanguagePackHeader hdr;
|
||||||
int i,j;
|
int i,j;
|
||||||
const CmdStruct *cs;
|
|
||||||
char param[256];
|
|
||||||
int argno;
|
|
||||||
|
|
||||||
f = fopen(filename, "wb");
|
f = fopen(filename, "wb");
|
||||||
if (f == NULL) Fatal("can't open %s", filename);
|
if (f == NULL) Fatal("can't open %s", filename);
|
||||||
|
@ -957,21 +1102,23 @@ static void WriteLangfile(const char *filename, int show_todo)
|
||||||
|
|
||||||
for(i = 0; i != 32; i++) {
|
for(i = 0; i != 32; i++) {
|
||||||
for(j = 0; j != in_use[i]; j++) {
|
for(j = 0; j != in_use[i]; j++) {
|
||||||
int idx = (i<<11)+j;
|
LangString *ls = _strings[(i<<11)+j];
|
||||||
char *str;
|
|
||||||
|
Case *casep;
|
||||||
|
char *cmdp;
|
||||||
|
|
||||||
// For undefined strings, just set that it's an empty string
|
// For undefined strings, just set that it's an empty string
|
||||||
if (_strname[idx] == NULL) {
|
if (ls == NULL) {
|
||||||
WriteLength(f, 0);
|
WriteLength(f, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cur_ident = _strname[idx];
|
_cur_ident = ls->name;
|
||||||
|
|
||||||
// Produce a message if a string doesn't have a translation.
|
// Produce a message if a string doesn't have a translation.
|
||||||
if (show_todo && _translated[idx] == NULL) {
|
if (show_todo && ls->translated == NULL) {
|
||||||
if (show_todo == 2) {
|
if (show_todo == 2) {
|
||||||
Warning("'%s' is untranslated", _strname[idx]);
|
Warning("'%s' is untranslated", ls->name);
|
||||||
} else {
|
} else {
|
||||||
const char *s = "<TODO> ";
|
const char *s = "<TODO> ";
|
||||||
while(*s) PutByte(*s++);
|
while(*s) PutByte(*s++);
|
||||||
|
@ -979,37 +1126,48 @@ static void WriteLangfile(const char *filename, int show_todo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the strings and stuff from the english command string
|
// Extract the strings and stuff from the english command string
|
||||||
ExtractCommandString(&_cur_pcs, _master[idx], false);
|
ExtractCommandString(&_cur_pcs, ls->english, false);
|
||||||
|
|
||||||
str = _translated[idx] ? _translated[idx] : _master[idx];
|
if (ls->translated_case || ls->translated) {
|
||||||
_cur_argidx = 0;
|
casep = ls->translated_case;
|
||||||
|
cmdp = ls->translated;
|
||||||
while (*str != '\0') {
|
} else {
|
||||||
// Process characters as they are until we encounter a {
|
casep = ls->english_case;
|
||||||
if (*str != '{') {
|
cmdp = ls->english;
|
||||||
PutByte(*str++);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cs = ParseCommandString(&str, param, &argno, false);
|
|
||||||
if (cs == NULL) break;
|
|
||||||
|
|
||||||
// For params that consume values, we need to handle the argindex properly
|
|
||||||
if (cs->consumes) {
|
|
||||||
// Check if we need to output a move-param command
|
|
||||||
if (argno!=-1 && argno != _cur_argidx) {
|
|
||||||
_cur_argidx = argno;
|
|
||||||
PutArgidxCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output the one from the master string... it's always accurate.
|
|
||||||
cs = _cur_pcs.cmd[_cur_argidx++];
|
|
||||||
if (!cs)
|
|
||||||
Fatal("cs == NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
cs->proc(param, cs->value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (casep) {
|
||||||
|
Case *c;
|
||||||
|
int num;
|
||||||
|
// Need to output a case-switch.
|
||||||
|
// It has this format
|
||||||
|
// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
|
||||||
|
// Each LEN is printed using 2 bytes in big endian order.
|
||||||
|
PutByte(0x9E);
|
||||||
|
// Count the number of cases
|
||||||
|
for(num=0,c=casep; c; c=c->next) num++;
|
||||||
|
PutByte(num);
|
||||||
|
|
||||||
|
// Write each case
|
||||||
|
for(c=casep; c; c=c->next) {
|
||||||
|
int pos;
|
||||||
|
PutByte(c->caseidx);
|
||||||
|
// Make some space for the 16-bit length
|
||||||
|
pos = _put_pos;
|
||||||
|
PutByte(0);
|
||||||
|
PutByte(0);
|
||||||
|
// Write string
|
||||||
|
PutCommandString(c->string);
|
||||||
|
PutByte(0); // terminate with a zero
|
||||||
|
// Fill in the length
|
||||||
|
_put_buf[pos] = (_put_pos - (pos + 2)) >> 8;
|
||||||
|
_put_buf[pos+1] = (_put_pos - (pos + 2)) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmdp)
|
||||||
|
PutCommandString(cmdp);
|
||||||
|
|
||||||
WriteLength(f, _put_pos);
|
WriteLength(f, _put_pos);
|
||||||
fwrite(_put_buf, 1, _put_pos, f);
|
fwrite(_put_buf, 1, _put_pos, f);
|
||||||
_put_pos = 0;
|
_put_pos = 0;
|
||||||
|
|
118
strings.c
118
strings.c
|
@ -16,7 +16,7 @@ static char *StationGetSpecialString(char *buff, int x);
|
||||||
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed);
|
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed);
|
||||||
static char *GetSpecialPlayerNameString(char *buff, int ind, const int32 *argv);
|
static char *GetSpecialPlayerNameString(char *buff, int ind, const int32 *argv);
|
||||||
|
|
||||||
static char *FormatString(char *buff, const char *str, const int32 *argv);
|
static char *FormatString(char *buff, const char *str, const int32 *argv, uint casei);
|
||||||
|
|
||||||
extern const char _openttd_revision[];
|
extern const char _openttd_revision[];
|
||||||
|
|
||||||
|
@ -173,12 +173,16 @@ static const char *GetStringPtr(StringID string)
|
||||||
return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
|
return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
|
||||||
}
|
}
|
||||||
|
|
||||||
char *GetStringWithArgs(char *buffr, StringID string, const int32 *argv)
|
// 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.
|
||||||
|
char *GetStringWithArgs(char *buffr, uint string, const int32 *argv)
|
||||||
{
|
{
|
||||||
uint index = string & 0x7FF;
|
uint index = string & 0x7FF;
|
||||||
uint tab = string >> 11;
|
uint tab = (string >> 11) & 0x1F;
|
||||||
|
|
||||||
if (!string) {
|
if (!(string & 0xFFFF)) {
|
||||||
error("!invalid string id 0 in GetString");
|
error("!invalid string id 0 in GetString");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +208,7 @@ char *GetStringWithArgs(char *buffr, StringID string, const int32 *argv)
|
||||||
return strecpy(buffr, _bound_strings[index], NULL);
|
return strecpy(buffr, _bound_strings[index], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FormatString(buffr, _userstring, NULL);
|
return FormatString(buffr, _userstring, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= _langtab_num[tab])
|
if (index >= _langtab_num[tab])
|
||||||
|
@ -213,7 +217,7 @@ char *GetStringWithArgs(char *buffr, StringID string, const int32 *argv)
|
||||||
"Probably because an old version of the .lng file.\n", string
|
"Probably because an old version of the .lng file.\n", string
|
||||||
);
|
);
|
||||||
|
|
||||||
return FormatString(buffr, GetStringPtr(string), argv);
|
return FormatString(buffr, GetStringPtr(string&0xFFFF), argv, string >> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *GetString(char *buffr, StringID string)
|
char *GetString(char *buffr, StringID string)
|
||||||
|
@ -503,10 +507,11 @@ static const char *ParseStringChoice(const char *b, uint form, char *dst, int *d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *FormatString(char *buff, const char *str, const int32 *argv)
|
static char *FormatString(char *buff, const char *str, const int32 *argv, uint casei)
|
||||||
{
|
{
|
||||||
byte b;
|
byte b;
|
||||||
const int32 *argv_orig = argv;
|
const int32 *argv_orig = argv;
|
||||||
|
uint modifier = 0;
|
||||||
|
|
||||||
while ((b = *str++) != '\0') {
|
while ((b = *str++) != '\0') {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
|
@ -532,7 +537,7 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
buff += len;
|
buff += len;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x7E: // {NUMU16}, {INT32}
|
case 0x7E: // {NUM}
|
||||||
buff = FormatNoCommaNumber(buff, GetInt32(&argv));
|
buff = FormatNoCommaNumber(buff, GetInt32(&argv));
|
||||||
break;
|
break;
|
||||||
case 0x7F: // {CURRENCY}
|
case 0x7F: // {CURRENCY}
|
||||||
|
@ -562,7 +567,6 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x85 is used as escape character..
|
// 0x85 is used as escape character..
|
||||||
case 0x85:
|
case 0x85:
|
||||||
switch (*str++) {
|
switch (*str++) {
|
||||||
|
@ -590,32 +594,37 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
}
|
}
|
||||||
case 5: { /* {STRING1} */
|
case 5: { /* {STRING1} */
|
||||||
// String that consumes ONE argument
|
// String that consumes ONE argument
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1));
|
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1));
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 6: { /* {STRING2} */
|
case 6: { /* {STRING2} */
|
||||||
// String that consumes TWO arguments
|
// String that consumes TWO arguments
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2));
|
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2));
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 7: { /* {STRING3} */
|
case 7: { /* {STRING3} */
|
||||||
// String that consumes THREE arguments
|
// String that consumes THREE arguments
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3));
|
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3));
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 8: { /* {STRING4} */
|
case 8: { /* {STRING4} */
|
||||||
// String that consumes FOUR arguments
|
// String that consumes FOUR arguments
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4));
|
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4));
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 9: { /* {STRING5} */
|
case 9: { /* {STRING5} */
|
||||||
// String that consumes FIVE arguments
|
// String that consumes FIVE arguments
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5));
|
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5));
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,14 +645,16 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
// The string STR_INDUSTRY_PATTERN controls the formatting
|
// The string STR_INDUSTRY_PATTERN controls the formatting
|
||||||
args[0] = i->town->index;
|
args[0] = i->town->index;
|
||||||
args[1] = i->type + STR_4802_COAL_MINE;
|
args[1] = i->type + STR_4802_COAL_MINE;
|
||||||
buff = FormatString(buff, GetStringPtr(STR_INDUSTRY_FORMAT), args);
|
buff = FormatString(buff, GetStringPtr(STR_INDUSTRY_FORMAT), args, modifier >> 24);
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 12: { // {VOLUME}
|
case 12: { // {VOLUME}
|
||||||
buff = FormatCommaNumber(buff, GetInt32(&argv) * 1000);
|
buff = FormatCommaNumber(buff, GetInt32(&argv) * 1000);
|
||||||
buff = strecpy(buff, " ", NULL);
|
buff = strecpy(buff, " ", NULL);
|
||||||
buff = strecpy(buff, GetStringPtr(STR_LITERS), NULL);
|
buff = FormatString(buff, GetStringPtr(STR_LITERS), NULL, modifier >> 24);
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,6 +669,20 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 14: { // {DATE_TINY}
|
||||||
|
buff = FormatTinyDate(buff, GetInt32(&argv));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 15: { // {CARGO}
|
||||||
|
// Layout now is:
|
||||||
|
// 8bit - cargo type
|
||||||
|
// 16-bit - cargo count
|
||||||
|
StringID cargo_str = _cargoc.names_long[GetInt32(&argv)];
|
||||||
|
buff = GetStringWithArgs(buff, cargo_str, argv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error("!invalid escape sequence in string");
|
error("!invalid escape sequence in string");
|
||||||
}
|
}
|
||||||
|
@ -668,28 +693,34 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// This sets up the gender for the string.
|
// This sets up the gender for the string.
|
||||||
// We just ignore this one. It's used somewhere else.
|
// We just ignore this one. It's used in {G 0 Der Die Das} to determine the case.
|
||||||
case 0x87: // {GENDER 0}
|
case 0x87: // {GENDER 0}
|
||||||
str++;
|
str++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x88: {// {STRING}
|
case 0x88: {// {STRING}
|
||||||
StringID str = GetInt32(&argv);
|
uint str = modifier + GetInt32(&argv);
|
||||||
// WARNING. It's prohibited for the included string to consume any arguments.
|
// WARNING. It's prohibited for the included string to consume any arguments.
|
||||||
// For included strings that consume argument, you should use STRING1, STRING2 etc.
|
// 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
|
// To debug stuff you can set argv to NULL and it will tell you
|
||||||
buff = GetStringWithArgs(buff, str, argv);
|
buff = GetStringWithArgs(buff, str, argv);
|
||||||
|
modifier = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x99: { // {CARGO}
|
case 0x99: { // {WAYPOINT}
|
||||||
// Layout now is:
|
int32 temp[2];
|
||||||
// 8bit - cargo type
|
Waypoint *wp = GetWaypoint(GetInt32(&argv));
|
||||||
// 16-bit - cargo count
|
StringID str;
|
||||||
StringID cargo_str = _cargoc.names_long[GetInt32(&argv)];
|
if (wp->string != STR_NULL) {
|
||||||
buff = GetStringWithArgs(buff, cargo_str, argv);
|
str = wp->string;
|
||||||
break;
|
} else {
|
||||||
}
|
temp[0] = wp->town_index;
|
||||||
|
temp[1] = wp->town_cn + 1;
|
||||||
|
str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
|
||||||
|
}
|
||||||
|
buff = GetStringWithArgs(buff, str, temp);
|
||||||
|
} break;
|
||||||
|
|
||||||
case 0x9A: { // {STATION}
|
case 0x9A: { // {STATION}
|
||||||
Station *st;
|
Station *st;
|
||||||
|
@ -720,22 +751,27 @@ static char *FormatString(char *buff, const char *str, const int32 *argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x9D: { // {WAYPOINT}
|
case 0x9D: { // {SETCASE}
|
||||||
int32 temp[2];
|
// This is a pseudo command, it's outputted when someone does {STRING.ack}
|
||||||
Waypoint *wp = GetWaypoint(GetInt32(&argv));
|
// The modifier is added to all subsequent GetStringWithArgs that accept the modifier.
|
||||||
StringID str;
|
modifier = (byte)*str++ << 24;
|
||||||
if (wp->string != STR_NULL) {
|
break;
|
||||||
str = wp->string;
|
}
|
||||||
} else {
|
|
||||||
temp[0] = wp->town_index;
|
|
||||||
temp[1] = wp->town_cn + 1;
|
|
||||||
str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
|
|
||||||
}
|
|
||||||
buff = GetStringWithArgs(buff, str, temp);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 0x9E: { // {DATE_TINY}
|
case 0x9E: { // {Used to implement case switching}
|
||||||
buff = FormatTinyDate(buff, GetInt32(&argv));
|
// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
|
||||||
|
// Each LEN is printed using 2 bytes in big endian order.
|
||||||
|
uint num = (byte)*str++;
|
||||||
|
while (num) {
|
||||||
|
if (str[0] == casei) {
|
||||||
|
// Found the case, adjust str pointer and continue
|
||||||
|
str += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Otherwise skip to the next case
|
||||||
|
str += 3 + (str[1] << 8) + str[2];
|
||||||
|
num--;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ static inline char* InlineString(char* buf, uint16 string)
|
||||||
}
|
}
|
||||||
|
|
||||||
char *GetString(char *buffr, uint16 string);
|
char *GetString(char *buffr, uint16 string);
|
||||||
char *GetStringWithArgs(char *buffr, uint16 string, const int32 *argv);
|
char *GetStringWithArgs(char *buffr, uint string, const int32 *argv);
|
||||||
|
|
||||||
void InjectDParam(int amount);
|
void InjectDParam(int amount);
|
||||||
int32 GetParamInt32(void);
|
int32 GetParamInt32(void);
|
||||||
|
|
Loading…
Reference in New Issue