summaryrefslogtreecommitdiff
path: root/strgen
diff options
context:
space:
mode:
authorludde <ludde@openttd.org>2005-07-15 14:53:44 +0000
committerludde <ludde@openttd.org>2005-07-15 14:53:44 +0000
commit2fa79c9b4d140f78e5e6392aded7beebeec5c230 (patch)
treeae57d82a4f417509705d577ea29c99c80b9e579b /strgen
parent8f71864bef912a9cd8b8f649c5acbe638d48fc24 (diff)
downloadopenttd-2fa79c9b4d140f78e5e6392aded7beebeec5c230.tar.xz
(svn r2572) - Codechange: [string] Changed string system so it's not as dependent on decode_parameters
- Feature: [strgen] Allow changing the order of parameters in translated strings. - Use {1:TOWN} syntax to set the order. - Codechange: [strgen] Rewrote lots of strgen internals.
Diffstat (limited to 'strgen')
-rw-r--r--strgen/strgen.c866
1 files changed, 474 insertions, 392 deletions
diff --git a/strgen/strgen.c b/strgen/strgen.c
index eaf75ef0c..0b2b19c07 100644
--- a/strgen/strgen.c
+++ b/strgen/strgen.c
@@ -34,66 +34,79 @@ typedef struct CmdStruct {
const char *cmd;
ParseCmdProc proc;
long value;
+ int8 consumes;
} CmdStruct;
-typedef struct LineName {
- struct LineName *hash_next;
- int value;
- char str[VARARRAY_SIZE];
-} LineName;
-
static int _cur_line;
-static bool _warnings;
+static int _errors, _warnings;
+
+static char *_strname[65536]; // Name of the string, NULL if not exists
+static char *_master[65536]; // English text
+static char *_translated[65536]; // Translated text
+static uint16 _hash_next[65536]; // next hash entry
+
+#define HASH_SIZE 32767
+static uint16 _hash_head[HASH_SIZE];
+
+static byte _put_buf[4096];
+static int _put_pos;
+static int _next_string_id;
static uint32 _hash;
-static char _lang_name[32];
-static char _lang_ownname[32];
-static char _lang_isocode[16];
+static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
-#define HASH_SIZE 1023
-static LineName *_hash_head[HASH_SIZE];
static uint HashStr(const char *s)
{
uint hash = 0;
- for (; *s; s++) hash = ((hash << 3) | (hash >> 29)) ^ *s;
+ for(; *s; s++)
+ hash = ((hash << 3) | (hash >> 29)) ^ *s;
return hash % HASH_SIZE;
}
static void HashAdd(const char *s, int value)
{
- uint len = strlen(s) + 1;
- LineName *ln = malloc(sizeof(*ln) + len);
uint hash = HashStr(s);
-
- ln->hash_next = _hash_head[hash];
- _hash_head[hash] = ln;
- ln->value = value;
- memcpy(ln->str, s, len);
+ _hash_next[value] = _hash_head[hash];
+ _hash_head[hash] = value + 1;
}
static int HashFind(const char *s)
{
- LineName *ln;
-
- for (ln = _hash_head[HashStr(s)]; ln != NULL; ln = ln->hash_next) {
- if (strcmp(ln->str, s) == 0) return ln->value;
+ int idx = _hash_head[HashStr(s)];
+ while (--idx >= 0) {
+ if (!strcmp(_strname[idx], s)) return idx;
+ idx = _hash_next[idx];
}
return -1;
}
-static void warning(const char *s, ...)
+
+static void CDECL Warning(const char *s, ...)
{
char buf[1024];
va_list va;
va_start(va, s);
vsprintf(buf, s, va);
va_end(va);
- fprintf(stderr, "%d: ERROR: %s\n", _cur_line, buf);
- _warnings = true;
+ fprintf(stderr, "%d: Warning: %s\n", _cur_line, buf);
+ _warnings++;
}
-static void NORETURN error(const char *s, ...)
+
+static void CDECL Error(const char *s, ...)
+{
+ char buf[1024];
+ va_list va;
+ va_start(va, s);
+ vsprintf(buf, s, va);
+ va_end(va);
+ fprintf(stderr, "%d: Error: %s\n", _cur_line, buf);
+ _errors++;
+}
+
+
+static void NORETURN CDECL Fatal(const char *s, ...)
{
char buf[1024];
va_list va;
@@ -104,6 +117,7 @@ static void NORETURN error(const char *s, ...)
exit(1);
}
+
static void ttd_strlcpy(char *dst, const char *src, size_t len)
{
assert(len > 0);
@@ -113,219 +127,198 @@ static void ttd_strlcpy(char *dst, const char *src, size_t len)
}
-// first byte tells if it's english.lng or a custom language file
-// second part is the name of the string
-// third part is the actual string contents
-static char *allstr[65536];
-
-static byte _put_buf[4096];
-static int _put_pos;
-static int _next_string_id;
-
-static void put_byte(byte c)
+static void PutByte(byte c)
{
if (_put_pos == lengthof(_put_buf))
- error("Put buffer too small");
+ Fatal("Put buffer too small");
_put_buf[_put_pos++] = c;
}
-static void emit_buf(int ent)
-{
- char *s;
-
- if (ent < 0 || ent >= 0x10000) {
- warning("Invalid string ID %d\n", ent);
- return;
- }
-
- if (allstr[ent] != 0) {
- warning("Duplicate string ID %d\n", ent);
- return;
- }
-
- // Allocate the string, and put the uint16 before with the length.
- s = (char*)malloc(sizeof(uint16) + _put_pos);
- *((uint16*)s) = _put_pos;
- memcpy(s + sizeof(uint16), _put_buf, _put_pos);
-
- allstr[ent] = s;
-
- _put_pos = 0;
-}
static void EmitSingleByte(char *buf, int value)
{
- if (*buf != '\0') warning("Ignoring trailing letters in command");
- put_byte((byte)value);
+ if (*buf != '\0')
+ Warning("Ignoring trailing letters in command");
+ PutByte((byte)value);
}
+
static void EmitEscapedByte(char *buf, int value)
{
- if (*buf != '\0') warning("Ignoring trailing letters in command");
- put_byte((byte)0x85);
- put_byte((byte)value);
+ if (*buf != '\0')
+ Warning("Ignoring trailing letters in command");
+ PutByte((byte)0x85);
+ PutByte((byte)value);
}
-static void EmitStringInl(char *buf, int value)
-{
- int id;
-
- if (*buf >= '0' && *buf <= '9') {
- id = strtol(buf, NULL, 0);
- if (id < 0 || id >= 0x10000) {
- warning("Invalid inline num %s\n", buf);
- return;
- }
- } else {
- id = HashFind(buf);
- if (id == -1) {
- warning("Invalid inline string '%s'", buf);
- return;
- }
- }
-
- put_byte(0x81);
- put_byte((byte)(id & 0xFF));
- put_byte((byte)(id >> 8));
-}
-
static void EmitSetX(char *buf, int value)
{
char *err;
int x = strtol(buf, &err, 0);
-
- if (*err != '\0') error("SetX param invalid");
- put_byte(1);
- put_byte((byte)x);
+ if (*err != 0)
+ Fatal("SetX param invalid");
+ PutByte(1);
+ PutByte((byte)x);
}
+
static void EmitSetXY(char *buf, int value)
{
char *err;
- int x;
- int y;
+ int x,y;
x = strtol(buf, &err, 0);
- if (*err != '\0') error("SetXY param invalid");
- y = strtol(err + 1, &err, 0);
- if (*err != '\0') error("SetXY param invalid");
+ if (*err != 0) Fatal("SetXY param invalid");
+ y = strtol(err+1, &err, 0);
+ if (*err != 0) Fatal("SetXY param invalid");
- put_byte(0x1F);
- put_byte((byte)x);
- put_byte((byte)y);
+ PutByte(0x1F);
+ PutByte((byte)x);
+ PutByte((byte)y);
}
+
static const CmdStruct _cmd_structs[] = {
// Update position
- {"SETX", EmitSetX, 1},
- {"SETXY", EmitSetXY, 2},
+ {"SETX", EmitSetX, 1, 0},
+ {"SETXY", EmitSetXY, 2, 0},
// Font size
- {"TINYFONT", EmitSingleByte, 8},
- {"BIGFONT", EmitSingleByte, 9},
+ {"TINYFONT", EmitSingleByte, 8, 0},
+ {"BIGFONT", EmitSingleByte, 9, 0},
// New line
- {"", EmitSingleByte, 10},
+ {"", EmitSingleByte, 10, 0},
// Colors
- {"BLUE", EmitSingleByte, 15},
- {"SILVER", EmitSingleByte, 16},
- {"GOLD", EmitSingleByte, 17},
- {"RED", EmitSingleByte, 18},
- {"PURPLE", EmitSingleByte, 19},
- {"LTBROWN", EmitSingleByte, 20},
- {"ORANGE", EmitSingleByte, 21},
- {"GREEN", EmitSingleByte, 22},
- {"YELLOW", EmitSingleByte, 23},
- {"DKGREEN", EmitSingleByte, 24},
- {"CREAM", EmitSingleByte, 25},
- {"BROWN", EmitSingleByte, 26},
- {"WHITE", EmitSingleByte, 27},
- {"LTBLUE", EmitSingleByte, 28},
- {"GRAY", EmitSingleByte, 29},
- {"DKBLUE", EmitSingleByte, 30},
- {"BLACK", EmitSingleByte, 31},
+ {"BLUE", EmitSingleByte, 15, 0},
+ {"SILVER", EmitSingleByte, 16, 0},
+ {"GOLD", EmitSingleByte, 17, 0},
+ {"RED", EmitSingleByte, 18, 0},
+ {"PURPLE", EmitSingleByte, 19, 0},
+ {"LTBROWN", EmitSingleByte, 20, 0},
+ {"ORANGE", EmitSingleByte, 21, 0},
+ {"GREEN", EmitSingleByte, 22, 0},
+ {"YELLOW", EmitSingleByte, 23, 0},
+ {"DKGREEN", EmitSingleByte, 24, 0},
+ {"CREAM", EmitSingleByte, 25, 0},
+ {"BROWN", EmitSingleByte, 26, 0},
+ {"WHITE", EmitSingleByte, 27, 0},
+ {"LTBLUE", EmitSingleByte, 28, 0},
+ {"GRAY", EmitSingleByte, 29, 0},
+ {"DKBLUE", EmitSingleByte, 30, 0},
+ {"BLACK", EmitSingleByte, 31, 0},
// 0x7B=123 is the LAST special character we may use.
// Numbers
- {"COMMA", EmitSingleByte, 0x7B},
-
- {"NUMU16", EmitSingleByte, 0x7E}, // These two do the same thing nowadays
- {"INT32", EmitSingleByte, 0x7E}, // signed 32 bit integer
+ {"COMMA", EmitSingleByte, 0x7B, 1}, // Number with comma
+ {"NUM", EmitSingleByte, 0x7E, 1}, // Signed number
- {"CURRENCY", EmitSingleByte, 0x7F},
+ {"CURRENCY", EmitSingleByte, 0x7F, 1},
// 0x85
- {"CURRCOMPACT", EmitEscapedByte, 0}, // compact currency (32 bits)
- {"REV", EmitEscapedByte, 2}, // openttd revision string
- {"SHORTCARGO", EmitEscapedByte, 3}, // short cargo description, only ### tons, or ### litres
- {"CURRCOMPACT64", EmitEscapedByte, 4}, // compact currency 64 bits
+ {"CURRCOMPACT", EmitEscapedByte, 0, 1}, // compact currency (32 bits)
+ {"REV", EmitEscapedByte, 2, 0}, // openttd revision string
+ {"SHORTCARGO", EmitEscapedByte, 3, 2}, // short cargo description, only ### tons, or ### litres
+ {"CURRCOMPACT64", EmitEscapedByte, 4, 2}, // compact currency 64 bits
+
+ {"COMPANY", EmitEscapedByte, 5, 1}, // company string. This is actually a {STRING1}
+ // The first string includes the second string.
+
+ {"PLAYERNAME", EmitEscapedByte, 5, 1}, // playername string. This is actually a {STRING1}
+ // The first string includes the second string.
+
+ {"VEHICLE", EmitEscapedByte, 5, 1}, // playername string. This is actually a {STRING1}
+ // The first string includes the second string.
+
+
+ {"STRING1", EmitEscapedByte, 5, 1}, // included string that consumes ONE argument
+ {"STRING2", EmitEscapedByte, 6, 2}, // included string that consumes TWO arguments
+ {"STRING3", EmitEscapedByte, 7, 3}, // included string that consumes THREE arguments
+ {"STRING4", EmitEscapedByte, 8, 4}, // included string that consumes FOUR arguments
+ {"STRING5", EmitEscapedByte, 9, 5}, // included string that consumes FIVE arguments
+
+ {"STATIONFEATURES", EmitEscapedByte, 10, 1}, // station features string, icons of the features
- {"STRINL", EmitStringInl, 0x81},
+ {"DATE_LONG", EmitSingleByte, 0x82, 1},
+ {"DATE_SHORT", EmitSingleByte, 0x83, 1},
- {"DATE_LONG", EmitSingleByte, 0x82},
- {"DATE_SHORT", EmitSingleByte, 0x83},
+ {"VELOCITY", EmitSingleByte, 0x84, 1},
- {"VELOCITY", EmitSingleByte, 0x84},
- {"SKIP16", EmitSingleByte, 0x85},
- {"SKIP", EmitSingleByte, 0x86},
- {"VOLUME", EmitSingleByte, 0x87},
+ {"SKIP", EmitSingleByte, 0x86, 1},
+ {"VOLUME", EmitSingleByte, 0x87, 1},
- {"STRING", EmitSingleByte, 0x88},
+ {"STRING", EmitSingleByte, 0x88, 1},
- {"CARGO", EmitSingleByte, 0x99},
- {"STATION", EmitSingleByte, 0x9A},
- {"TOWN", EmitSingleByte, 0x9B},
- {"CURRENCY64", EmitSingleByte, 0x9C},
- {"WAYPOINT", EmitSingleByte, 0x9D}, // waypoint name
- {"DATE_TINY", EmitSingleByte, 0x9E},
+ {"CARGO", EmitSingleByte, 0x99, 2},
+ {"STATION", EmitSingleByte, 0x9A, 1},
+ {"TOWN", EmitSingleByte, 0x9B, 1},
+ {"CURRENCY64", EmitSingleByte, 0x9C, 2},
+ {"WAYPOINT", EmitSingleByte, 0x9D, 1}, // waypoint name
+ {"DATE_TINY", EmitSingleByte, 0x9E, 1},
// 0x9E=158 is the LAST special character we may use.
- {"UPARROW", EmitSingleByte, 0xA0},
- {"POUNDSIGN", EmitSingleByte, 0xA3},
- {"YENSIGN", EmitSingleByte, 0xA5},
- {"COPYRIGHT", EmitSingleByte, 0xA9},
- {"DOWNARROW", EmitSingleByte, 0xAA},
- {"CHECKMARK", EmitSingleByte, 0xAC},
- {"CROSS", EmitSingleByte, 0xAD},
- {"RIGHTARROW", EmitSingleByte, 0xAF},
-
- {"TRAIN", EmitSingleByte, 0xb4},
- {"LORRY", EmitSingleByte, 0xb5},
- {"BUS", EmitSingleByte, 0xb6},
- {"PLANE", EmitSingleByte, 0xb7},
- {"SHIP", EmitSingleByte, 0xb8},
-
- {"SMALLUPARROW", EmitSingleByte, 0xBC},
- {"SMALLDOWNARROW", EmitSingleByte, 0xBD},
- {"THREE_FOURTH", EmitSingleByte, 0xBE},
+ {"UPARROW", EmitSingleByte, 0xA0, 0},
+ {"POUNDSIGN", EmitSingleByte, 0xA3, 0},
+ {"YENSIGN", EmitSingleByte, 0xA5, 0},
+ {"COPYRIGHT", EmitSingleByte, 0xA9, 0},
+ {"DOWNARROW", EmitSingleByte, 0xAA, 0},
+ {"CHECKMARK", EmitSingleByte, 0xAC, 0},
+ {"CROSS", EmitSingleByte, 0xAD, 0},
+ {"RIGHTARROW", EmitSingleByte, 0xAF, 0},
+
+ {"TRAIN", EmitSingleByte, 0xb4, 0},
+ {"LORRY", EmitSingleByte, 0xb5, 0},
+ {"BUS", EmitSingleByte, 0xb6, 0},
+ {"PLANE", EmitSingleByte, 0xb7, 0},
+ {"SHIP", EmitSingleByte, 0xb8, 0},
+
+ {"SMALLUPARROW", EmitSingleByte, 0xBC, 0},
+ {"SMALLDOWNARROW", EmitSingleByte, 0xBD, 0},
+ {"THREE_FOURTH", EmitSingleByte, 0xBE, 0},
};
+
static const CmdStruct *FindCmd(const char *s, int len)
{
- const CmdStruct *cs;
-
- for (cs = _cmd_structs; cs != endof(_cmd_structs); cs++) {
- if (strncmp(cs->cmd, s, len) == 0 && cs->cmd[len] == '\0') return cs;
+ int i;
+ const CmdStruct *cs = _cmd_structs;
+ for(i=0; i != lengthof(_cmd_structs); i++, cs++) {
+ if (!strncmp(cs->cmd, s, len) && cs->cmd[len] == '\0')
+ return cs;
}
return NULL;
}
-/* Returns command struct of next command in str
- * NULL if command is invalid or if there are no more commands in str
- */
-static const CmdStruct *ParseCommandString(char **str, char *param, const char *errortext)
+// returns NULL on eof
+// else returns command struct
+static const CmdStruct *ParseCommandString(char **str, char *param, int *argno, bool show_err)
{
- char *s = *str;
- char *start;
- byte c;
+ char *s = *str, *start;
const CmdStruct *cmd;
+ int plen = 0;
+ byte c;
- for (; *s != '{'; s++) if (*s == '\0') return NULL;
- s++;
+ *argno = -1;
+
+ // Scan to the next command, exit if there's no next command.
+ for(; *s != '{'; s++) {
+ if (*s == '\0')
+ return NULL;
+ }
+ s++; // Skip past the {
+
+ if (*s >= '0' && *s <= '9') {
+ char *end;
+ *argno = strtoul(s, &end, 0);
+ if (*end != ':') {
+ Fatal("missing arg #");
+ }
+ s = end + 1;
+ }
// parse command name
start = s;
@@ -334,14 +327,14 @@ static const CmdStruct *ParseCommandString(char **str, char *param, const char *
if (c == '}' || c == ' ')
break;
if (c == '\0') {
- if (errortext) warning("Missing } from command '%s' in '%s'", start, errortext);
+ Error("Missing } from command '%s'", start);
return NULL;
}
}
cmd = FindCmd(start, s - start - 1);
if (cmd == NULL) {
- if (errortext) warning("Undefined command '%.*s' in '%s'", s - start - 1, start, errortext);
+ Error("Undefined command '%.*s'", s - start - 1, start);
return NULL;
}
@@ -351,13 +344,14 @@ static const CmdStruct *ParseCommandString(char **str, char *param, const char *
for(;;) {
c = *s++;
if (c == '}') break;
- if (c == 0) {
- if (errortext) warning("Missing } from command '%s' in '%s'", start, errortext);
+ if (c == '\0') {
+ Error("Missing } from command '%s'", start);
return NULL;
}
+ if ( s - start == 60)
+ Fatal("param command too long");
*param++ = c;
}
-
}
*param = 0;
@@ -367,48 +361,144 @@ static const CmdStruct *ParseCommandString(char **str, char *param, const char *
}
-static void handle_pragma(char *str)
+static void HandlePragma(char *str)
{
- if (memcmp(str, "id ", 3) == 0) {
+ if (!memcmp(str, "id ", 3)) {
_next_string_id = strtoul(str + 3, NULL, 0);
- } else if (memcmp(str, "name ", 5) == 0) {
+ } else if (!memcmp(str, "name ", 5)) {
ttd_strlcpy(_lang_name, str + 5, sizeof(_lang_name));
- } else if (memcmp(str, "ownname ", 8) == 0) {
+ } else if (!memcmp(str, "ownname ", 8)) {
ttd_strlcpy(_lang_ownname, str + 8, sizeof(_lang_ownname));
- } else if (memcmp(str, "isocode ", 8) == 0) {
+ } else if (!memcmp(str, "isocode ", 8)) {
ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
} else {
- error("unknown pragma '%s'", str);
+ Fatal("unknown pragma '%s'", str);
}
}
-static bool check_commands_match(char *a, char *b)
+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)
{
const CmdStruct *ar;
- const CmdStruct *br;
char param[100];
+ int argno;
+ int argidx = 0;
- do {
- ar = ParseCommandString(&a, param, NULL);
- br = ParseCommandString(&b, param, NULL);
+ memset(p, 0, sizeof(*p));
- // make sure they are identical
- if (ar != br) return false;
- } while (ar != NULL);
+ for(;;) {
+ // read until next command from a.
+ ar = ParseCommandString(&s, param, &argno, warnings);
+ if (ar == NULL)
+ break;
- return true;
+ // Sanity checking
+ if (argno != -1 && !ar->consumes) Fatal("Non consumer param can't have a paramindex");
+
+ if (ar->consumes) {
+ if (argno != -1)
+ argidx = argno;
+ if (argidx < 0 || argidx >= lengthof(p->cmd)) Fatal("invalid param idx %d", argidx);
+ if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) Fatal("duplicate param idx %d", argidx);
+
+ p->cmd[argidx++] = ar;
+ } else if (ar->cmd[0] != '\0') { // Ignore {}.. it can appear in any order.
+ 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].v = param[0]?strdup(param):"";
+ p->np++;
+ }
+ }
+}
+
+
+static const CmdStruct *TranslateCmdForCompare(const CmdStruct *a)
+{
+ if (!a) return NULL;
+
+ if (!strcmp(a->cmd, "STRING1") ||
+ !strcmp(a->cmd, "STRING2") ||
+ !strcmp(a->cmd, "STRING3") ||
+ !strcmp(a->cmd, "STRING4") ||
+ !strcmp(a->cmd, "STRING5"))
+ return FindCmd("STRING", 6);
+
+ if (!strcmp(a->cmd, "SKIP"))
+ return NULL;
+
+ return a;
}
-static void handle_string(char *str, bool master)
+
+static bool CheckCommandsMatch(char *a, char *b)
{
- char *s;
- char *t;
- char *r;
+ ParsedCommandStruct templ;
+ ParsedCommandStruct lang;
+ int i,j;
+ bool result = true;
+
+ ExtractCommandString(&templ, b, true);
+ ExtractCommandString(&lang, a, true);
+
+ // For each string in templ, see if we find it in lang
+ if (templ.np != lang.np) {
+ Error("template string and language string have a different # of commands");
+ result = false;
+ }
+
+ for(i = 0; i < templ.np; i++) {
+ // see if we find it in lang, and zero it out
+ bool found = false;
+ for(j = 0; j < lang.np; j++) {
+ if (templ.pairs[i].a == lang.pairs[j].a &&
+ !strcmp(templ.pairs[i].v, lang.pairs[j].v)) {
+ // it was found in both. zero it out from lang so we don't find it again
+ lang.pairs[j].a = NULL;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ Error("Command '%s' exists in template file but not in language file", templ.pairs[i].a->cmd);
+ result = false;
+ }
+ }
+
+ // if we reach here, all non consumer commands match up.
+ // Check if the non consumer commands match up also.
+ for(i = 0; i < lengthof(templ.cmd); i++) {
+ if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
+ Error("Param idx #%d '%s' doesn't match with template command '%s'", i,
+ !lang.cmd[i] ? "<empty>" : lang.cmd[i]->cmd,
+ !templ.cmd[i] ? "<empty>" : templ.cmd[i]->cmd);
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+
+static void HandleString(char *str, bool master)
+{
+ char *s,*t;
int ent;
if (*str == '#') {
if (str[1] == '#' && str[2] != '#')
- handle_pragma(str + 2);
+ HandlePragma(str + 2);
return;
}
@@ -418,155 +508,136 @@ static void handle_string(char *str, bool master)
s = strchr(str, ':');
if (s == NULL) {
- warning("Line has no ':' delimiter");
+ Error("Line has no ':' delimiter");
return;
}
- // Trim spaces
- for (t = s; t > str && (t[-1] == ' ' || t[-1] == '\t'); t--);
- *t = '\0';
+ // Trim spaces.
+ // After this str points to the command name, and s points to the command contents
+ for(t = s; t > str && (t[-1]==' ' || t[-1]=='\t'); t--);
+ *t = 0;
+ s++;
+ // Check if this string already exists..
ent = HashFind(str);
- s++; // skip :
-
- // allocate string entry
- r = malloc(strlen(str) + strlen(s) + 3);
- *r = master;
- strcpy(r + 1, str);
- strcpy(r + 2 + strlen(str), s);
-
if (master) {
if (ent != -1) {
- warning("String name '%s' is used multiple times", str);
+ Error("String name '%s' is used multiple times", str);
return;
}
ent = _next_string_id++;
- if (allstr[ent]) {
- warning("String ID 0x%X for '%s' already in use by '%s'", ent, str, allstr[ent] + 1);
+ if (_strname[ent]) {
+ Error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strname[ent]);
return;
}
- allstr[ent] = r;
+ _strname[ent] = strdup(str);
+ _master[ent] = strdup(s);
// add to hash table
HashAdd(str, ent);
} else {
if (ent == -1) {
- warning("String name '%s' does not exist in master file", str);
- _warnings = 0; // non-fatal
- return;
- }
-
- if (!allstr[ent][0]) {
- warning("String name '%s' is used multiple times", str);
- _warnings = 0; // non-fatal
+ Error("String name '%s' does not exist in master file", str);
return;
}
- // check that the commands match
- if (!check_commands_match(s, allstr[ent] + 2 + strlen(allstr[ent] + 1))) {
- fprintf(stderr, "Warning: String name '%s' does not match the layout of the master string\n", str);
- _warnings = 0; // non-fatal
+ if (_translated[ent]) {
+ Error("String name '%s' is used multiple times", str);
return;
}
if (s[0] == ':' && s[1] == '\0') {
- allstr[ent][0] = 0; // use string from master file legitiately
- free(r);
+ // Special syntax :: means we should just inherit the master string
+ _translated[ent] = strdup(_master[ent]);
} else {
- free(allstr[ent]);
- allstr[ent] = r;
+ // check that the commands match
+ if (!CheckCommandsMatch(s, _master[ent])) {
+ Error("String name '%s' does not match the layout of the master string\n", str);
+ return;
+ }
+ _translated[ent] = strdup(s);
}
}
}
-static uint32 my_hash_str(uint32 hash, const char *s)
-{
- for (; *s != '\0'; s++) {
- hash = ((hash << 3) | (hash >> 29)) ^ *s;
- if (hash & 1) hash = (hash >> 1) ^ 0xDEADBEEF; else hash >>= 1;
- }
- return hash;
+static void rstrip(char *buf)
+{
+ int i = strlen(buf);
+ while (i>0 && (buf[i-1]=='\r' || buf[i-1]=='\n' || buf[i-1] == ' ')) i--;
+ buf[i] = 0;
}
-static void parse_file(const char *file, bool english)
+
+static void ParseFile(const char *file, bool english)
{
- char buf[2048];
- int i;
FILE *in;
+ char buf[2048];
in = fopen(file, "r");
- if (in == NULL) error("Cannot open file '%s'", file);
-
+ if (in == NULL) { Fatal("Cannot open file '%s'", file); }
_cur_line = 1;
while (fgets(buf, sizeof(buf),in) != NULL) {
- i = strlen(buf);
- while (i > 0 && (buf[i - 1] == '\r' || buf[i - 1] == '\n' || buf[i - 1] == ' ')) i--;
- buf[i] = '\0';
-
- handle_string(buf, english);
+ rstrip(buf);
+ HandleString(buf, english);
_cur_line++;
}
-
fclose(in);
+}
- // make a hash of the file to get a unique "version number"
- if (english) {
- uint32 hash = 0;
-
- for (i = 0; i != 65536; i++) {
- char *s = allstr[i];
- if (s != NULL) {
- const CmdStruct *cs;
+static uint32 MyHashStr(uint32 hash, const char *s)
+{
+ for(; *s; s++) {
+ hash = ((hash << 3) | (hash >> 29)) ^ *s;
+ if (hash & 1) hash = (hash>>1) ^ 0xDEADBEEF; else hash >>= 1;
+ }
+ return hash;
+}
- hash ^= i * 0x717239;
- if (hash & 1) hash = (hash >> 1) ^ 0xDEADBEEF; else hash >>= 1;
- hash = my_hash_str(hash, s + 1);
- s = s + 2 + strlen(s + 1);
- while ((cs = ParseCommandString(&s, buf, NULL)) != NULL) {
- hash ^= (cs - _cmd_structs) * 0x1234567;
- if (hash & 1) hash = (hash >> 1) ^ 0xF00BAA4; else hash >>= 1;
- }
+// make a hash of the file to get a unique "version number"
+static void MakeHashOfStrings()
+{
+ uint32 hash = 0;
+ char *s;
+ const CmdStruct *cs;
+ char buf[128];
+ int i;
+ int argno;
+
+ for(i = 0; i != 65536; i++) {
+ if ((s=_strname[i]) != NULL) {
+ hash ^= i * 0x717239;
+ if (hash & 1) hash = (hash>>1) ^ 0xDEADBEEF; else hash >>= 1;
+ hash = MyHashStr(hash, s + 1);
+
+ s = _master[i];
+ while ((cs = ParseCommandString(&s, buf, &argno, false)) != NULL) {
+ hash ^= (cs - _cmd_structs) * 0x1234567;
+ if (hash & 1) hash = (hash>>1) ^ 0xF00BAA4; else hash >>= 1;
}
}
- _hash = hash;
}
+ _hash = hash;
}
-static int count_inuse(int grp)
+
+static int CountInUse(int grp)
{
int i;
- for (i = 0x800; --i >= 0;) {
- if (allstr[(grp << 11) + i] != NULL) break;
+ for(i = 0x800; --i >= 0;) {
+ if (_strname[(grp<<11)+i] != NULL)
+ break;
}
return i + 1;
}
-static void check_all_used(void)
-{
- int i;
- LineName *ln;
- int num_warn = 10;
-
- for (i = 0; i != HASH_SIZE; i++) {
- for (ln = _hash_head[i]; ln != NULL; ln = ln->hash_next) {
- if (allstr[ln->value] == 0) {
- if (++num_warn < 50) {
- warning("String %s has no definition. Using NULL value", ln->str);
- }
- _put_pos = 0;
- emit_buf(ln->value);
- }
- }
- }
-}
-static void write_length(FILE *f, uint length)
+static void WriteLength(FILE *f, uint length)
{
if (length < 0xC0) {
fputc(length, f);
@@ -574,49 +645,12 @@ static void write_length(FILE *f, uint length)
fputc((length >> 8) | 0xC0, f);
fputc(length & 0xFF, f);
} else {
- error("string too long");
+ Fatal("string too long");
}
}
-static void gen_output(FILE *f)
-{
- uint16 in_use[32];
- uint16 in_use_file[32];
- int i;
- int j;
- int tot_str = 0;
-
- check_all_used();
-
- for (i = 0; i != 32; i++) {
- int n = count_inuse(i);
-
- in_use[i] = n;
- in_use_file[i] = TO_LE16(n);
- tot_str += n;
- }
-
- fwrite(in_use_file, 32 * sizeof(uint16), 1, f);
-
- for (i = 0; i != 32; i++) {
- for (j = 0; j != in_use[i]; j++) {
- char *s = allstr[(i << 11) + j];
- if (s == NULL) error("Internal error, s == NULL");
-
- write_length(f, *(uint16*)s);
- fwrite(s + sizeof(uint16), *(uint16*)s , 1, f);
- tot_str--;
- }
- }
-
- fputc(0, f); // write trailing nul character.
- if (tot_str != 0) {
- error("Internal error, tot_str != 0");
- }
-}
-
-static bool compare_files(const char *n1, const char *n2)
+bool CompareFiles(const char *n1, const char *n2)
{
FILE *f1, *f2;
char b1[4096];
@@ -627,13 +661,13 @@ static bool compare_files(const char *n1, const char *n2)
if (f2 == NULL) return false;
f1 = fopen(n1, "rb");
- if (f1 == NULL) error("can't open %s", n1);
+ if (f1 == NULL) Fatal("can't open %s", n1);
do {
l1 = fread(b1, 1, sizeof(b1), f1);
l2 = fread(b2, 1, sizeof(b2), f2);
- if (l1 != l2 || memcmp(b1, b2, l1) != 0) {
+ if (l1 != l2 || memcmp(b1, b2, l1)) {
fclose(f2);
fclose(f1);
return false;
@@ -645,7 +679,8 @@ static bool compare_files(const char *n1, const char *n2)
return true;
}
-static void write_strings_h(const char *filename)
+
+static void WriteStringsH(const char *filename)
{
FILE *out;
int i;
@@ -653,20 +688,20 @@ static void write_strings_h(const char *filename)
int lastgrp;
out = fopen("tmp.xxx", "w");
- if (out == NULL) error("can't open tmp.xxx");
+ if (out == NULL) { Fatal("can't open tmp.xxx"); }
fprintf(out, "enum {");
lastgrp = 0;
- for (i = 0; i != 65536; i++) {
- if (allstr[i]) {
+ for(i = 0; i != 65536; i++) {
+ if (_strname[i]) {
if (lastgrp != (i >> 11)) {
lastgrp = (i >> 11);
fprintf(out, "};\n\nenum {");
}
- fprintf(out, next == i ? "%s,\n" : "\n%s = 0x%X,\n", allstr[i] + 1, i);
+ fprintf(out, next == i ? "%s,\n" : "\n%s = 0x%X,\n", _strname[i], i);
next = i + 1;
}
}
@@ -679,9 +714,10 @@ static void write_strings_h(const char *filename)
"\tLANGUAGE_PACK_VERSION = 0x%X,\n"
"};\n", (uint)_hash);
+
fclose(out);
- if (compare_files("tmp.xxx", filename)) {
+ if (CompareFiles("tmp.xxx", filename)) {
// files are equal. tmp.xxx is not needed
unlink("tmp.xxx");
} else {
@@ -689,26 +725,45 @@ static void write_strings_h(const char *filename)
#if defined(WIN32)
unlink(filename);
#endif
- if (rename("tmp.xxx", filename) == -1) error("rename() failed");
+ if (rename("tmp.xxx", filename) == -1) Fatal("rename() failed");
+ }
+}
+
+
+static void PutArgidxCommand(ParsedCommandStruct *pcs, int argidx)
+{
+ int i, sum;
+
+ if (argidx >= lengthof(pcs->cmd))
+ Fatal("invalid argidx %d", argidx);
+
+ for(i = sum = 0; i < argidx; i++) {
+ const CmdStruct *cs = pcs->cmd[i++];
+ sum += cs ? cs->consumes : 1;
}
+
+ PutByte(0x7C);
+ PutByte((byte)sum);
}
-static void write_langfile(const char *filename, int show_todo)
+
+static void WriteLangfile(const char *filename, int show_todo)
{
FILE *f;
int in_use[32];
LanguagePackHeader hdr;
- int i;
- int j;
+ int i,j;
const CmdStruct *cs;
char param[128];
+ int argno;
+ ParsedCommandStruct pcs;
f = fopen(filename, "wb");
- if (f == NULL) error("can't open %s", filename);
+ if (f == NULL) Fatal("can't open %s", filename);
memset(&hdr, 0, sizeof(hdr));
- for (i = 0; i != 32; i++) {
- int n = count_inuse(i);
+ for(i = 0; i != 32; i++) {
+ int n = CountInUse(i);
in_use[i] = n;
hdr.offsets[i] = TO_LE16(n);
}
@@ -722,39 +777,63 @@ static void write_langfile(const char *filename, int show_todo)
fwrite(&hdr, sizeof(hdr), 1, f);
- for (i = 0; i != 32; i++) {
- for (j = 0; j != in_use[i]; j++) {
- char *s = allstr[(i << 11) + j];
-
- if (s == NULL) {
- write_length(f, 0);
- } else {
- // move to string
- char* str = s + 2 + strlen(s + 1);
-
- if (show_todo && s[0]) {
- if (show_todo == 2) {
- fprintf(stderr, "Warning:%s: String '%s' is untranslated\n", filename, s + 1);
- } else {
- const char *s = "<TODO> ";
- while(*s) put_byte(*s++);
- }
+ for(i = 0; i != 32; i++) {
+ for(j = 0; j != in_use[i]; j++) {
+ int idx = (i<<11)+j;
+ char *str;
+ int argidx;
+
+ // For undefined strings, just set that it's an empty string
+ if (_strname[idx] == NULL) {
+ WriteLength(f, 0);
+ continue;
+ }
+
+ // Produce a message if a string doesn't have a translation.
+ if (show_todo && _translated[idx] == NULL) {
+ if (show_todo == 2) {
+ Warning("'%s' is untranslated", _strname[idx]);
+ } else {
+ const char *s = "<TODO> ";
+ while(*s) PutByte(*s++);
}
+ }
+
+ // Extract the strings and stuff from the english command string
+ ExtractCommandString(&pcs, _master[idx], false);
- while (*str != '\0') {
- if (*str != '{') {
- put_byte(*str++);
- } else {
- cs = ParseCommandString(&str, param, s[0] ? "english.lng" : filename);
- if (cs == NULL) break;
- cs->proc(param, cs->value);
+ str = _translated[idx] ? _translated[idx] : _master[idx];
+ argidx = 0;
+
+ while (*str != '\0') {
+ // Process characters as they are until we encounter a {
+ if (*str != '{') {
+ 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 != argidx) {
+ argidx = argno;
+ PutArgidxCommand(&pcs, argidx);
}
+
+ // Output the one from the master string... it's always accurate.
+ cs = pcs.cmd[argidx++];
+ if (!cs)
+ Fatal("cs == NULL");
}
- write_length(f, _put_pos);
- fwrite(_put_buf, 1, _put_pos, f);
- _put_pos = 0;
+ cs->proc(param, cs->value);
}
+
+ WriteLength(f, _put_pos);
+ fwrite(_put_buf, 1, _put_pos, f);
+ _put_pos = 0;
}
}
@@ -763,53 +842,56 @@ static void write_langfile(const char *filename, int show_todo)
fclose(f);
}
+
int CDECL main(int argc, char* argv[])
{
+ char *r;
+ char buf[256];
int show_todo = 0;
- if (argc > 1 &&
- (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0)) {
+ if (argc > 1 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))) {
puts("$Revision$");
return 0;
}
- if (argc > 1 && strcmp(argv[1], "-t") == 0) {
+ if (argc > 1 && !strcmp(argv[1], "-t")) {
show_todo = 1;
- argc--;
- argv++;
+ argc--, argv++;
}
- if (argc > 1 && strcmp(argv[1], "-w") == 0) {
+ if (argc > 1 && !strcmp(argv[1], "-w")) {
show_todo = 2;
- argc--;
- argv++;
+ argc--, argv++;
}
if (argc == 1) {
// parse master file
- parse_file("lang/english.txt", true);
- if (_warnings) return 1;
+ ParseFile("lang/english.txt", true);
+ MakeHashOfStrings();
+ if (_errors) return 1;
// write english.lng and strings.h
- write_langfile("lang/english.lng", 0);
- write_strings_h("table/strings.h");
+ WriteLangfile("lang/english.lng", 0);
+ WriteStringsH("table/strings.h");
+
} else if (argc == 2) {
- char buf[256];
- char *r;
+ ParseFile("lang/english.txt", true);
+ MakeHashOfStrings();
+ ParseFile(argv[1], false);
+
+ if (_errors) return 1;
- parse_file("lang/english.txt", true);
- parse_file(argv[1], false);
- if (_warnings) return 1;
strcpy(buf, argv[1]);
r = strrchr(buf, '.');
- if (r == NULL || strcmp(r, ".txt") != 0) r = strchr(buf, 0);
+ if (!r || strcmp(r, ".txt")) r = strchr(buf, 0);
strcpy(r, ".lng");
- write_langfile(buf, show_todo);
+ WriteLangfile(buf, show_todo);
} else {
fprintf(stderr, "invalid arguments\n");
}
return 0;
}
+