core/CTranslator.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jan 19 02:14:50 2007 +0000 (2007-01-19)
changeset 317 e5342c8a0cf4
parent 306 7c90e7938609
child 334 ea5c1f855ce0
permissions -rw-r--r--
added new path format functions
exposed helpers as ISourceMod
dvander@276
     1
#include <stdarg.h>
dvander@300
     2
#include <stdlib.h>
dvander@276
     3
#include "CTranslator.h"
dvander@276
     4
#include "CLogger.h"
dvander@276
     5
#include "CTextParsers.h"
dvander@276
     6
#include "LibrarySys.h"
dvander@276
     7
#include "sm_stringutil.h"
dvander@300
     8
#include "sourcemod.h"
dvander@276
     9
dvander@276
    10
CTranslator g_Translator;
dvander@276
    11
dvander@276
    12
struct trans_t
dvander@276
    13
{
dvander@276
    14
	int stridx;
dvander@276
    15
	int fmt_order;
dvander@276
    16
};
dvander@276
    17
dvander@276
    18
struct phrase_t
dvander@276
    19
{
dvander@276
    20
	int fmt_list;
dvander@276
    21
	unsigned int fmt_count;
dvander@276
    22
	unsigned int fmt_bytes;
dvander@276
    23
	int trans_tbl;
dvander@276
    24
	unsigned int translations;
dvander@276
    25
};
dvander@276
    26
dvander@276
    27
CPhraseFile::CPhraseFile(CTranslator *pTranslator, const char *file)
dvander@276
    28
{
dvander@276
    29
	m_pStringTab = pTranslator->GetStringTable();
dvander@276
    30
	m_pMemory = m_pStringTab->GetMemTable();
dvander@276
    31
	m_LangCount = pTranslator->GetLanguageCount();
dvander@276
    32
	m_File.assign(file);
dvander@276
    33
	m_pTranslator = pTranslator;
dvander@276
    34
	m_pPhraseLookup = NULL;
dvander@276
    35
}
dvander@276
    36
dvander@276
    37
CPhraseFile::~CPhraseFile()
dvander@276
    38
{
dvander@276
    39
	if (m_pPhraseLookup)
dvander@276
    40
	{
dvander@276
    41
		sm_trie_destroy(m_pPhraseLookup);
dvander@276
    42
	}
dvander@276
    43
}
dvander@276
    44
dvander@276
    45
void CPhraseFile::ParseError(const char *message, ...)
dvander@276
    46
{
dvander@276
    47
	char buffer[1024];
dvander@276
    48
	va_list ap;
dvander@276
    49
	va_start(ap, message);
dvander@276
    50
	vsnprintf(buffer, sizeof(buffer), message, ap);
dvander@276
    51
	va_end(ap);
dvander@276
    52
dvander@276
    53
	m_ParseError.assign(buffer);
dvander@276
    54
}
dvander@276
    55
dvander@276
    56
void CPhraseFile::ParseWarning(const char *message, ...)
dvander@276
    57
{
dvander@276
    58
	char buffer[1024];
dvander@276
    59
	va_list ap;
dvander@276
    60
	va_start(ap, message);
dvander@276
    61
	vsnprintf(buffer, sizeof(buffer), message, ap);
dvander@276
    62
	va_end(ap);
dvander@276
    63
dvander@276
    64
	if (!m_FileLogged)
dvander@276
    65
	{
dvander@306
    66
		g_Logger.LogError("[SM] Warning(s) encountered in translation file \"%s\"", m_File.c_str());
dvander@276
    67
		m_FileLogged;
dvander@276
    68
	}
dvander@276
    69
dvander@306
    70
	g_Logger.LogError("[SM] %s", message);
dvander@276
    71
}
dvander@276
    72
dvander@276
    73
void CPhraseFile::ReparseFile()
dvander@276
    74
{
dvander@276
    75
	if (m_pPhraseLookup)
dvander@276
    76
	{
dvander@276
    77
		sm_trie_destroy(m_pPhraseLookup);
dvander@276
    78
	}
dvander@276
    79
	m_pPhraseLookup = sm_trie_create();
dvander@276
    80
dvander@276
    81
	m_LangCount = m_pTranslator->GetLanguageCount();
dvander@276
    82
dvander@276
    83
	if (!m_LangCount)
dvander@276
    84
	{
dvander@276
    85
		return;
dvander@276
    86
	}
dvander@276
    87
dvander@276
    88
	SMCParseError err;
dvander@276
    89
	char path[PLATFORM_MAX_PATH+1];
dvander@317
    90
	g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "translations/%s", m_File.c_str());
dvander@276
    91
	unsigned int line=0, col=0;
dvander@276
    92
dvander@276
    93
	if ((err=g_TextParser.ParseFile_SMC(path, this, &line, &col)) != SMCParse_Okay)
dvander@276
    94
	{
dvander@276
    95
		const char *msg = g_TextParser.GetSMCErrorString(err);
dvander@276
    96
		if (!msg)
dvander@276
    97
		{
dvander@276
    98
			msg = m_ParseError.c_str();
dvander@276
    99
		}
dvander@276
   100
dvander@306
   101
		g_Logger.LogError("[SM] Fatal error encountered parsing translation file \"%s\"", m_File.c_str());
dvander@306
   102
		g_Logger.LogError("[SM] Error (line %d, column %d): %s", line, col, msg);
dvander@276
   103
	}
dvander@276
   104
}
dvander@276
   105
dvander@276
   106
void CPhraseFile::ReadSMC_ParseStart()
dvander@276
   107
{
dvander@276
   108
	m_CurPhrase = -1;
dvander@276
   109
	m_ParseState = PPS_None;
dvander@276
   110
	m_CurLine = 0;
dvander@276
   111
	m_FileLogged = false;
dvander@276
   112
	m_LastPhraseString.clear();
dvander@276
   113
}
dvander@276
   114
dvander@276
   115
SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline)
dvander@276
   116
{
dvander@276
   117
	m_CurLine = curline;
dvander@276
   118
dvander@276
   119
	return SMCParse_Continue;
dvander@276
   120
}
dvander@276
   121
dvander@276
   122
SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes)
dvander@276
   123
{
dvander@276
   124
	bool recognized = false;
dvander@276
   125
	if (m_ParseState == PPS_None)
dvander@276
   126
	{
dvander@276
   127
		if (strcmp(name, "Phrases") == 0)
dvander@276
   128
		{
dvander@276
   129
			m_ParseState = PPS_Phrases;
dvander@276
   130
			recognized = true;
dvander@276
   131
		}
dvander@276
   132
	} else if (m_ParseState == PPS_Phrases) {
dvander@276
   133
		m_ParseState = PPS_InPhrase;
dvander@276
   134
		recognized = true;
dvander@276
   135
dvander@276
   136
		phrase_t *pPhrase;
dvander@276
   137
		m_CurPhrase = m_pMemory->CreateMem(sizeof(phrase_t), (void **)&pPhrase);
dvander@276
   138
dvander@276
   139
		/* Create the reverse lookup */
dvander@276
   140
		if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase)))
dvander@276
   141
		{
dvander@276
   142
			ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, m_CurLine);
dvander@276
   143
			/* :IDEA: prevent memory waste by seeking backwards in memtable? */
dvander@276
   144
			m_CurPhrase = -1;
dvander@276
   145
		} else {
dvander@276
   146
			/* Initialize new phrase */
dvander@276
   147
			trans_t *pTrans;
dvander@276
   148
	
dvander@276
   149
			pPhrase->fmt_count = 0;
dvander@276
   150
			pPhrase->fmt_list = -1;
dvander@276
   151
			pPhrase->trans_tbl = m_pMemory->CreateMem(sizeof(trans_t) * m_LangCount, (void **)&pTrans);
dvander@276
   152
			pPhrase->translations = 0;
dvander@276
   153
			pPhrase->fmt_bytes = 0;
dvander@276
   154
	
dvander@276
   155
			for (unsigned int i=0; i<m_LangCount; i++)
dvander@276
   156
			{
dvander@276
   157
				pTrans[i].stridx = -1;
dvander@276
   158
			}
dvander@276
   159
			m_LastPhraseString.assign(name);
dvander@276
   160
		}
dvander@276
   161
	} else if (m_ParseState == PPS_InPhrase) {
dvander@276
   162
		ParseError("Phrase sections may not have sub-sections");
dvander@276
   163
		return SMCParse_HaltFail;
dvander@276
   164
	}
dvander@276
   165
dvander@276
   166
	if (!recognized)
dvander@276
   167
	{
dvander@276
   168
		ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, m_CurLine);
dvander@276
   169
	}
dvander@276
   170
dvander@276
   171
	return SMCParse_Continue;
dvander@276
   172
}
dvander@276
   173
dvander@276
   174
SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
dvander@276
   175
{
dvander@276
   176
	/* See if we are ignoring a phrase */
dvander@276
   177
	if (!m_CurPhrase)
dvander@276
   178
	{
dvander@276
   179
		return SMCParse_Continue;
dvander@276
   180
	}
dvander@276
   181
dvander@276
   182
	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
dvander@276
   183
	
dvander@276
   184
	if (key[0] == '#' && strcmp(key, "#format") == 0)
dvander@276
   185
	{
dvander@276
   186
		if (pPhrase->fmt_list != -1)
dvander@276
   187
		{
dvander@276
   188
			ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine);
dvander@276
   189
			return SMCParse_Continue;
dvander@276
   190
		}
dvander@276
   191
dvander@276
   192
		if (pPhrase->translations > 0)
dvander@276
   193
		{
dvander@276
   194
			ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine);
dvander@276
   195
			return SMCParse_Continue;
dvander@276
   196
		}
dvander@276
   197
dvander@276
   198
		/* Do an initial browsing for error checking and what not */
dvander@276
   199
		enum ParseStates
dvander@276
   200
		{
dvander@276
   201
			Parse_None,
dvander@276
   202
			Parse_Index,
dvander@276
   203
			Parse_Format,
dvander@276
   204
		};
dvander@276
   205
dvander@276
   206
		ParseStates state = Parse_None;
dvander@276
   207
dvander@276
   208
		const char *old_value = value;
dvander@276
   209
		while (*value != '\0')
dvander@276
   210
		{
dvander@276
   211
			if (state == Parse_None)
dvander@276
   212
			{
dvander@276
   213
				if (*value == '{')
dvander@276
   214
				{
dvander@276
   215
					pPhrase->fmt_count++;
dvander@276
   216
					state = Parse_Index;
dvander@276
   217
				} else if (*value == ',') {
dvander@276
   218
					/* Do nothing */
dvander@276
   219
				} else {
dvander@276
   220
					unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
dvander@276
   221
					if (bytes != 1 || !isalpha(*value))
dvander@276
   222
					{
dvander@276
   223
						ParseWarning("Invalid token '%c' in #format property on line %d.", *value, m_CurLine);
dvander@276
   224
					}
dvander@276
   225
				}
dvander@276
   226
			} else if (state == Parse_Index) {
dvander@276
   227
				if (*value == ':')
dvander@276
   228
				{
dvander@276
   229
					state = Parse_Format;
dvander@276
   230
					if (value - old_value >= 15)
dvander@276
   231
					{
dvander@276
   232
						ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", m_CurLine);
dvander@276
   233
						m_CurPhrase = -1;
dvander@276
   234
						return SMCParse_Continue;
dvander@276
   235
					}
dvander@276
   236
				} else {
dvander@276
   237
					unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
dvander@276
   238
					if (bytes != 1 || !isdigit(*value))
dvander@276
   239
					{
dvander@276
   240
						ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
dvander@276
   241
									*value,
dvander@276
   242
									m_CurLine);
dvander@276
   243
						m_CurPhrase = -1;
dvander@276
   244
						return SMCParse_Continue;
dvander@276
   245
					}
dvander@276
   246
				}
dvander@276
   247
			} else if (state == Parse_Format) {
dvander@276
   248
				if (*value == '}')
dvander@276
   249
				{
dvander@276
   250
					state = Parse_None;
dvander@276
   251
				}
dvander@276
   252
			}
dvander@276
   253
			value++;
dvander@276
   254
		}
dvander@276
   255
dvander@276
   256
		if (state != Parse_None)
dvander@276
   257
		{
dvander@276
   258
			/* Moose clam cow. */
dvander@276
   259
			ParseWarning("Unterminated format string on line %d, phrase will be ignored.", m_CurLine);
dvander@276
   260
			m_CurPhrase = -1;
dvander@276
   261
			return SMCParse_Continue;
dvander@276
   262
		}
dvander@276
   263
		value = old_value;
dvander@276
   264
dvander@276
   265
		/* Allocate the format table */
dvander@276
   266
		char fmt_buf[16];
dvander@276
   267
		int *fmt_list;
dvander@276
   268
		pPhrase->fmt_list = m_pMemory->CreateMem(sizeof(int) * pPhrase->fmt_count, (void **)&fmt_list);
dvander@276
   269
dvander@276
   270
		/* Initialize */
dvander@276
   271
		for (size_t i=0; i<pPhrase->fmt_count; i++)
dvander@276
   272
		{
dvander@276
   273
			fmt_list[i] = -1;
dvander@276
   274
		}
dvander@276
   275
dvander@276
   276
		/* Now we need to read again... this time into the format buffer */
dvander@276
   277
		const char *in_ptr = value;
dvander@276
   278
		const char *idx_ptr = NULL;
dvander@276
   279
		char *out_ptr = NULL;
dvander@276
   280
		unsigned int cur_idx = 0;
dvander@276
   281
		state = Parse_None;
dvander@276
   282
		while (*in_ptr != '\0')
dvander@276
   283
		{
dvander@276
   284
			if (state == Parse_None)
dvander@276
   285
			{
dvander@276
   286
				if (*in_ptr == '{')
dvander@276
   287
				{
dvander@276
   288
					state = Parse_Index;
dvander@276
   289
					idx_ptr = NULL;
dvander@276
   290
				}
dvander@276
   291
			} else if (state == Parse_Index) {
dvander@276
   292
				if (*in_ptr == ':')
dvander@276
   293
				{
dvander@276
   294
					/* Check the number! */
dvander@276
   295
					if (!idx_ptr)
dvander@276
   296
					{
dvander@276
   297
						ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", m_CurLine);
dvander@276
   298
						m_CurPhrase = -1;
dvander@276
   299
						return SMCParse_Continue;
dvander@276
   300
					}
dvander@276
   301
					long idx = strtol(idx_ptr, NULL, 10);
dvander@276
   302
					if (idx < 1 || idx > (long)pPhrase->fmt_count)
dvander@276
   303
					{
dvander@276
   304
						ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
dvander@276
   305
						m_CurPhrase = -1;
dvander@276
   306
						return SMCParse_Continue;
dvander@276
   307
					} else if (fmt_list[idx - 1] != -1) {
dvander@276
   308
						ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
dvander@276
   309
						m_CurPhrase = -1;
dvander@276
   310
						return SMCParse_Continue;
dvander@276
   311
					}
dvander@276
   312
					cur_idx = (unsigned int)idx;
dvander@276
   313
					state = Parse_Format;
dvander@276
   314
					out_ptr = NULL;
dvander@276
   315
				} else if (!idx_ptr) {
dvander@276
   316
					idx_ptr = in_ptr;
dvander@276
   317
				}
dvander@276
   318
			} else if (state == Parse_Format) {
dvander@276
   319
				if (*in_ptr == '}')
dvander@276
   320
				{
dvander@276
   321
					if (!out_ptr)
dvander@276
   322
					{
dvander@276
   323
						ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", m_CurLine);
dvander@276
   324
						m_CurPhrase = -1;
dvander@276
   325
						return SMCParse_Continue;
dvander@276
   326
					}
dvander@276
   327
					*out_ptr = '\0';
dvander@276
   328
					state = Parse_None;
dvander@276
   329
					/* Now, add this to our table */
dvander@276
   330
					fmt_list[cur_idx - 1] = m_pStringTab->AddString(fmt_buf);
dvander@276
   331
					pPhrase->fmt_bytes += strlen(fmt_buf);
dvander@276
   332
				} else {
dvander@276
   333
					if (!out_ptr)
dvander@276
   334
					{
dvander@276
   335
						out_ptr = fmt_buf;
dvander@276
   336
						*out_ptr++ = '%';
dvander@276
   337
					}
dvander@276
   338
					/* Check length ... */
dvander@276
   339
					if (out_ptr - fmt_buf >= sizeof(fmt_buf) - 1)
dvander@276
   340
					{
dvander@276
   341
						ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
dvander@276
   342
									 m_CurLine);
dvander@276
   343
						m_CurPhrase = -1;
dvander@276
   344
						return SMCParse_Continue;
dvander@276
   345
					}
dvander@276
   346
					*out_ptr++ = *in_ptr;
dvander@276
   347
				}
dvander@276
   348
			}
dvander@276
   349
			in_ptr++;
dvander@276
   350
		}
dvander@276
   351
dvander@276
   352
		/* If we've gotten here, we only accepted unique indexes in a range.
dvander@276
   353
		 * Therefore, the list has to be completely filled.  Double check anyway.
dvander@276
   354
		 */
dvander@276
   355
		for (size_t i=0; i<pPhrase->fmt_count; i++)
dvander@276
   356
		{
dvander@276
   357
			if (fmt_list[i] == -1)
dvander@276
   358
			{
dvander@276
   359
				ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
dvander@276
   360
							 i + 1,
dvander@276
   361
							 m_CurLine);
dvander@276
   362
				m_CurPhrase = -1;
dvander@276
   363
				return SMCParse_Continue;
dvander@276
   364
			}
dvander@276
   365
		}
dvander@276
   366
	} else {
dvander@276
   367
		size_t len = strlen(key);
dvander@276
   368
		if (len != 2)
dvander@276
   369
		{
dvander@276
   370
			ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine);
dvander@276
   371
			return SMCParse_Continue;
dvander@276
   372
		}
dvander@276
   373
dvander@276
   374
		unsigned int lang;
dvander@276
   375
		if (!m_pTranslator->GetLanguageByCode(key, &lang))
dvander@276
   376
		{
dvander@276
   377
			/* Ignore if we don't have a language.
dvander@276
   378
			 * :IDEA: issue a one-time alert?
dvander@276
   379
			 */
dvander@276
   380
			return SMCParse_Continue;
dvander@276
   381
		}
dvander@276
   382
dvander@276
   383
		/* See how many bytes we need for this string, then allocate.
dvander@276
   384
		 * NOTE: THIS SHOULD GUARANTEE THAT WE DO NOT NEED TO NEED TO SIZE CHECK 
dvander@276
   385
		 */
dvander@276
   386
		len = strlen(value) + pPhrase->fmt_bytes + 1;
dvander@276
   387
		char *out_buf;
dvander@276
   388
		int out_idx;
dvander@276
   389
dvander@276
   390
		out_idx = m_pMemory->CreateMem(len, (void **)&out_buf);
dvander@276
   391
dvander@276
   392
		int *fmt_order;
dvander@276
   393
		int *fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
dvander@276
   394
		trans_t *pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
dvander@276
   395
dvander@276
   396
		pTrans = &pTrans[lang];
dvander@276
   397
		pTrans->stridx = out_idx;
dvander@276
   398
dvander@276
   399
		bool params[MAX_TRANSLATE_PARAMS];
dvander@276
   400
dvander@276
   401
		/* Build the format order list, if necessary */
dvander@276
   402
		if (fmt_list)
dvander@276
   403
		{
dvander@276
   404
			pTrans->fmt_order = m_pMemory->CreateMem(pPhrase->fmt_count * sizeof(int), (void **)&fmt_order);
dvander@276
   405
dvander@276
   406
			for (unsigned int i=0; i<pPhrase->fmt_count; i++)
dvander@276
   407
			{
dvander@276
   408
				fmt_order[i] = -1;
dvander@276
   409
			}
dvander@276
   410
			memset(&params[0], 0, sizeof(params));
dvander@276
   411
		}
dvander@276
   412
dvander@276
   413
		const char *in_ptr = value;
dvander@276
   414
		char *out_ptr = out_buf;
dvander@276
   415
		unsigned int order_idx = 0;
dvander@276
   416
		while (*in_ptr != '\0')
dvander@276
   417
		{
dvander@276
   418
			if (*in_ptr == '\\')
dvander@276
   419
			{
dvander@276
   420
				switch (*(in_ptr + 1))
dvander@276
   421
				{
dvander@276
   422
				case '\n':
dvander@276
   423
					*out_ptr++ = '\n';
dvander@276
   424
					break;
dvander@276
   425
				case '\t':
dvander@276
   426
					*out_ptr++ = '\t';
dvander@276
   427
					break;
dvander@276
   428
				case '\r':
dvander@276
   429
					*out_ptr++ = '\r';
dvander@276
   430
					break;
dvander@276
   431
				case '{':
dvander@276
   432
					*out_ptr++ = '{';
dvander@276
   433
					break;
dvander@276
   434
				default:
dvander@276
   435
					/* Copy both bytes since we don't know what's going on */
dvander@276
   436
					*out_ptr++ = *in_ptr++;
dvander@276
   437
					*out_ptr++ = *in_ptr;
dvander@276
   438
					break;
dvander@276
   439
				}
dvander@276
   440
				/* Skip past the last byte read */
dvander@276
   441
				in_ptr++;
dvander@276
   442
			} else if (*in_ptr == '{' && fmt_list != NULL) {
dvander@276
   443
				/* Search for parameters if this is a formatted string */
dvander@276
   444
				const char *scrap_in_point = in_ptr;
dvander@276
   445
				const char *digit_start = ++in_ptr;
dvander@276
   446
				unsigned int bytes;
dvander@276
   447
				while (*in_ptr != '\0')
dvander@276
   448
				{
dvander@276
   449
					bytes = g_TextParser.GetUTF8CharBytes(in_ptr);
dvander@276
   450
					if (bytes != 1)
dvander@276
   451
					{
dvander@276
   452
						goto scrap_point;
dvander@276
   453
					}
dvander@276
   454
					if (*in_ptr == '}')
dvander@276
   455
					{
dvander@276
   456
						/* Did we get an index? */
dvander@276
   457
						if (in_ptr == digit_start)
dvander@276
   458
						{
dvander@276
   459
							goto scrap_point;
dvander@276
   460
						}
dvander@276
   461
						/* Is it valid? */
dvander@276
   462
						long idx = strtol(digit_start, NULL, 10);
dvander@276
   463
						if (idx < 1 || idx > (int)pPhrase->fmt_count)
dvander@276
   464
						{
dvander@276
   465
							goto scrap_point;
dvander@276
   466
						}
dvander@276
   467
						if (params[idx-1])
dvander@276
   468
						{
dvander@276
   469
							goto scrap_point;
dvander@276
   470
						}
dvander@276
   471
dvander@276
   472
						/* We're safe to insert the string.  First mark order. */
dvander@276
   473
						fmt_order[order_idx++] = (int)idx - 1;
dvander@276
   474
						/* Now concatenate */
dvander@276
   475
						out_ptr += sprintf(out_ptr, "%s", m_pStringTab->GetString(fmt_list[idx-1]));
dvander@276
   476
						/* Mark as used */
dvander@276
   477
						params[idx-1] = true;
dvander@276
   478
dvander@276
   479
						goto cont_loop;
dvander@276
   480
					}
dvander@276
   481
					in_ptr++;
dvander@276
   482
				}
dvander@276
   483
scrap_point:
dvander@276
   484
				/* Pretend none of this ever happened.  Move along now! */
dvander@276
   485
				in_ptr = scrap_in_point;
dvander@276
   486
				*out_ptr++ = *in_ptr;
dvander@276
   487
			} else {
dvander@276
   488
				*out_ptr++ = *in_ptr;
dvander@276
   489
			}
dvander@276
   490
cont_loop:
dvander@276
   491
			in_ptr++;
dvander@276
   492
		}
dvander@276
   493
		*out_ptr = '\0';
dvander@276
   494
		pPhrase->translations++;
dvander@276
   495
	}
dvander@276
   496
dvander@276
   497
	return SMCParse_Continue;
dvander@276
   498
}
dvander@276
   499
dvander@276
   500
SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
dvander@276
   501
{
dvander@276
   502
	if (m_ParseState == PPS_InPhrase)
dvander@276
   503
	{
dvander@276
   504
		if (m_CurPhrase == -1 && m_LastPhraseString.size())
dvander@276
   505
		{
dvander@276
   506
			sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
dvander@276
   507
		}
dvander@276
   508
		m_CurPhrase = -1;
dvander@276
   509
		m_ParseState = PPS_Phrases;
dvander@276
   510
		m_LastPhraseString.assign("");
dvander@276
   511
	} else if (m_ParseState == PPS_Phrases) {
dvander@276
   512
		m_ParseState = PPS_None;
dvander@276
   513
	}
dvander@276
   514
dvander@276
   515
	return SMCParse_Continue;
dvander@276
   516
}
dvander@276
   517
dvander@276
   518
void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed)
dvander@276
   519
{
dvander@276
   520
	/* Check to see if we have any dangling phrases that weren't completed, and scrap them */
dvander@276
   521
	if ((halted || failed) && m_LastPhraseString.size())
dvander@276
   522
	{
dvander@276
   523
		sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
dvander@276
   524
	}
dvander@276
   525
}
dvander@276
   526
dvander@276
   527
const char *CPhraseFile::GetTranslation(const Translation *pTrans, int **fmt_order, unsigned int *fmt_count)
dvander@276
   528
{
dvander@276
   529
	if (pTrans->lang_id >= m_LangCount)
dvander@276
   530
	{
dvander@276
   531
		return NULL;
dvander@276
   532
	}
dvander@276
   533
dvander@276
   534
	void *object;
dvander@276
   535
	if (!sm_trie_retrieve(m_pPhraseLookup, pTrans->szPhrase, &object))
dvander@276
   536
	{
dvander@276
   537
		return NULL;
dvander@276
   538
	}
dvander@276
   539
dvander@276
   540
	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(reinterpret_cast<int>(object));
dvander@276
   541
	trans_t *trans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
dvander@276
   542
dvander@276
   543
	trans = &trans[pTrans->lang_id];
dvander@276
   544
dvander@276
   545
	if (trans->stridx == -1)
dvander@276
   546
	{
dvander@276
   547
		return NULL;
dvander@276
   548
	}
dvander@276
   549
dvander@276
   550
	*fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order);
dvander@276
   551
	*fmt_count = pPhrase->fmt_count;
dvander@276
   552
dvander@276
   553
	return m_pStringTab->GetString(trans->stridx);
dvander@276
   554
}
dvander@276
   555
dvander@276
   556
const char *CPhraseFile::GetFilename()
dvander@276
   557
{
dvander@276
   558
	return m_File.c_str();
dvander@276
   559
}
dvander@276
   560
dvander@276
   561
CTranslator::CTranslator()
dvander@276
   562
{
dvander@276
   563
	m_pStringTab = new BaseStringTable(2048);
dvander@276
   564
	m_pLCodeLookup = sm_trie_create();
dvander@276
   565
}
dvander@276
   566
dvander@276
   567
CTranslator::~CTranslator()
dvander@276
   568
{
dvander@276
   569
	for (size_t i=0; i<m_Files.size(); i++)
dvander@276
   570
	{
dvander@276
   571
		delete m_Files[i];
dvander@276
   572
	}
dvander@276
   573
dvander@276
   574
	for (size_t i=0; i<m_Languages.size(); i++)
dvander@276
   575
	{
dvander@276
   576
		delete m_Languages[i];
dvander@276
   577
	}
dvander@276
   578
dvander@276
   579
	sm_trie_destroy(m_pLCodeLookup);
dvander@276
   580
dvander@276
   581
	delete m_pStringTab;
dvander@276
   582
}
dvander@276
   583
dvander@276
   584
bool CTranslator::GetLanguageByCode(const char *code, unsigned int *index)
dvander@276
   585
{
dvander@276
   586
	void *_index;
dvander@276
   587
dvander@276
   588
	if (!sm_trie_retrieve(m_pLCodeLookup, code, &_index))
dvander@276
   589
	{
dvander@276
   590
		return false;
dvander@276
   591
	}
dvander@276
   592
dvander@276
   593
	if (index)
dvander@276
   594
	{
dvander@276
   595
		*index = reinterpret_cast<unsigned int>(_index);
dvander@276
   596
	}
dvander@276
   597
dvander@276
   598
	return true;
dvander@276
   599
}
dvander@276
   600
dvander@276
   601
unsigned int CTranslator::GetLanguageCount()
dvander@276
   602
{
dvander@276
   603
	return (unsigned int)m_Languages.size();
dvander@276
   604
}
dvander@276
   605
dvander@276
   606
BaseStringTable *CTranslator::GetStringTable()
dvander@276
   607
{
dvander@276
   608
	return m_pStringTab;
dvander@276
   609
}
dvander@276
   610
dvander@276
   611
unsigned int CTranslator::FindOrAddPhraseFile(const char *phrase_file)
dvander@276
   612
{
dvander@276
   613
	for (size_t i=0; i<m_Files.size(); i++)
dvander@276
   614
	{
dvander@276
   615
		if (strcmp(m_Files[i]->GetFilename(), phrase_file) == 0)
dvander@276
   616
		{
dvander@276
   617
			return (unsigned int)i;
dvander@276
   618
		}
dvander@276
   619
	}
dvander@276
   620
dvander@276
   621
	CPhraseFile *pFile = new CPhraseFile(this, phrase_file);
dvander@276
   622
	unsigned int idx = (unsigned int)m_Files.size();
dvander@276
   623
	
dvander@276
   624
	m_Files.push_back(pFile);
dvander@276
   625
dvander@276
   626
	pFile->ReparseFile();
dvander@276
   627
dvander@276
   628
	return idx;
dvander@276
   629
}
dvander@276
   630
dvander@276
   631
void CTranslator::RebuildLanguageDatabase(const char *lang_header_file)
dvander@276
   632
{
dvander@276
   633
	/* Erase everything we have */
dvander@276
   634
	sm_trie_destroy(m_pLCodeLookup);
dvander@276
   635
	m_pLCodeLookup = sm_trie_create();
dvander@276
   636
	m_pStringTab->Reset();
dvander@276
   637
dvander@276
   638
	for (size_t i=0; i<m_Languages.size(); i++)
dvander@276
   639
	{
dvander@276
   640
		delete m_Languages[i];
dvander@276
   641
	}
dvander@276
   642
	m_Languages.clear();
dvander@276
   643
dvander@276
   644
	/* Start anew */
dvander@276
   645
	SMCParseError err;
dvander@276
   646
	unsigned int line=0, col=0;
dvander@276
   647
	if ((err=g_TextParser.ParseFile_SMC(lang_header_file, this, &line, &col)) != SMCParse_Okay)
dvander@276
   648
	{
dvander@276
   649
		const char *str_err = g_TextParser.GetSMCErrorString(err);
dvander@276
   650
		if (!str_err)
dvander@276
   651
		{
dvander@276
   652
			str_err = m_CustomError.c_str();
dvander@276
   653
		}
dvander@276
   654
dvander@306
   655
		g_Logger.LogError("[SM] Failed to parse language header file: \"%s\"", lang_header_file);
dvander@306
   656
		g_Logger.LogError("[SM] Parse error (line %d, column %d): %s", line, col, str_err);
dvander@276
   657
	}
dvander@276
   658
dvander@276
   659
	if (!m_Languages.size())
dvander@276
   660
	{
dvander@306
   661
		g_Logger.LogError("[SM] Fatal error, no languages found! Translation will not work.");
dvander@276
   662
	}
dvander@276
   663
dvander@276
   664
	for (size_t i=0; i<m_Files.size(); i++)
dvander@276
   665
	{
dvander@276
   666
		m_Files[i]->ReparseFile();
dvander@276
   667
	}
dvander@276
   668
}
dvander@276
   669
dvander@276
   670
void CTranslator::ReadSMC_ParseStart()
dvander@276
   671
{
dvander@276
   672
	m_InLanguageSection = false;
dvander@276
   673
	m_CustomError.clear();
dvander@276
   674
}
dvander@276
   675
dvander@276
   676
SMCParseResult CTranslator::ReadSMC_NewSection(const char *name, bool opt_quotes)
dvander@276
   677
{
dvander@276
   678
	if (!m_InLanguageSection)
dvander@276
   679
	{
dvander@276
   680
		if (strcmp(name, "Languages") == 0)
dvander@276
   681
		{
dvander@276
   682
			m_InLanguageSection = true;
dvander@276
   683
		}
dvander@276
   684
	}
dvander@276
   685
dvander@276
   686
	if (!m_InLanguageSection)
dvander@276
   687
	{
dvander@306
   688
		g_Logger.LogError("[SM] Warning: Unrecognized section \"%s\" in languages.cfg");
dvander@276
   689
	}
dvander@276
   690
dvander@276
   691
	return SMCParse_Continue;
dvander@276
   692
}
dvander@276
   693
dvander@276
   694
SMCParseResult CTranslator::ReadSMC_LeavingSection()
dvander@276
   695
{
dvander@276
   696
	m_InLanguageSection = false;
dvander@276
   697
dvander@276
   698
	return SMCParse_Continue;
dvander@276
   699
}
dvander@276
   700
dvander@276
   701
SMCParseResult CTranslator::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
dvander@276
   702
{
dvander@276
   703
	size_t len = strlen(key);
dvander@276
   704
dvander@276
   705
	if (len != 2)
dvander@276
   706
	{
dvander@306
   707
		g_Logger.LogError("[SM] Warning encountered parsing languages.cfg file.");
dvander@306
   708
		g_Logger.LogError("[SM] Invalid language code \"%s\" is being ignored.");
dvander@276
   709
	}
dvander@276
   710
dvander@276
   711
	Language *pLanguage = new Language;
dvander@276
   712
	unsigned int idx = m_Languages.size();
dvander@276
   713
dvander@276
   714
	strcpy(pLanguage->m_code2, key);
dvander@276
   715
	pLanguage->m_FullName = m_pStringTab->AddString(value);
dvander@276
   716
dvander@276
   717
	sm_trie_insert(m_pLCodeLookup, key, reinterpret_cast<void *>(idx));
dvander@276
   718
dvander@276
   719
	m_Languages.push_back(pLanguage);
dvander@276
   720
dvander@276
   721
	return SMCParse_Continue;
dvander@276
   722
}
dvander@276
   723
dvander@276
   724
size_t CTranslator::Translate(char *buffer, size_t maxlength, const Translation *pTrans)
dvander@276
   725
{
dvander@276
   726
	if (pTrans->file_id >= m_Files.size())
dvander@276
   727
	{
dvander@276
   728
		return 0;
dvander@276
   729
	}
dvander@276
   730
dvander@276
   731
	CPhraseFile *pFile = m_Files[pTrans->file_id];
dvander@276
   732
	unsigned int count;
dvander@276
   733
	int *order_table;
dvander@276
   734
	const char *str;
dvander@276
   735
dvander@276
   736
	if ((str=pFile->GetTranslation(pTrans, &order_table, &count)) == NULL)
dvander@276
   737
	{
dvander@276
   738
		return 0;
dvander@276
   739
	}
dvander@276
   740
dvander@276
   741
	void *params[MAX_TRANSLATE_PARAMS];
dvander@276
   742
dvander@276
   743
	/* Rewrite the parameter order */
dvander@276
   744
	for (unsigned int i=0; i<count; i++)
dvander@276
   745
	{
dvander@276
   746
		params[i] = pTrans->params[order_table[i]];
dvander@276
   747
	}
dvander@276
   748
dvander@276
   749
	return gnprintf(buffer, maxlength, str, params);
dvander@276
   750
}