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