core/Translator.cpp
changeset 591 86e97f3eda1f
child 592 c2a2c38f1331
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/core/Translator.cpp	Sat Mar 10 21:18:07 2007 +0000
     1.3 @@ -0,0 +1,818 @@
     1.4 +/**
     1.5 + * ===============================================================
     1.6 + * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     1.7 + * ===============================================================
     1.8 + *
     1.9 + * This file is not open source and may not be copied without explicit
    1.10 + * written permission of AlliedModders LLC.  This file may not be redistributed 
    1.11 + * in whole or significant part.
    1.12 + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    1.13 + *
    1.14 + * Version: $Id$
    1.15 + */
    1.16 +
    1.17 +#include <stdarg.h>
    1.18 +#include <stdlib.h>
    1.19 +#include <ctype.h>
    1.20 +#include "Translator.h"
    1.21 +#include "CLogger.h"
    1.22 +#include "TextParsers.h"
    1.23 +#include "LibrarySys.h"
    1.24 +#include "sm_stringutil.h"
    1.25 +#include "sourcemod.h"
    1.26 +
    1.27 +CTranslator g_Translator;
    1.28 +CPhraseFile *g_pCorePhrases = NULL;
    1.29 +
    1.30 +struct trans_t
    1.31 +{
    1.32 +	int stridx;
    1.33 +	int fmt_order;
    1.34 +};
    1.35 +
    1.36 +struct phrase_t
    1.37 +{
    1.38 +	int fmt_list;
    1.39 +	unsigned int fmt_count;
    1.40 +	unsigned int fmt_bytes;
    1.41 +	int trans_tbl;
    1.42 +	unsigned int translations;
    1.43 +};
    1.44 +
    1.45 +CPhraseFile::CPhraseFile(CTranslator *pTranslator, const char *file)
    1.46 +{
    1.47 +	m_pStringTab = pTranslator->GetStringTable();
    1.48 +	m_pMemory = m_pStringTab->GetMemTable();
    1.49 +	m_LangCount = pTranslator->GetLanguageCount();
    1.50 +	m_File.assign(file);
    1.51 +	m_pTranslator = pTranslator;
    1.52 +	m_pPhraseLookup = NULL;
    1.53 +}
    1.54 +
    1.55 +CPhraseFile::~CPhraseFile()
    1.56 +{
    1.57 +	if (m_pPhraseLookup)
    1.58 +	{
    1.59 +		sm_trie_destroy(m_pPhraseLookup);
    1.60 +	}
    1.61 +}
    1.62 +
    1.63 +void CPhraseFile::ParseError(const char *message, ...)
    1.64 +{
    1.65 +	char buffer[1024];
    1.66 +	va_list ap;
    1.67 +	va_start(ap, message);
    1.68 +	vsnprintf(buffer, sizeof(buffer), message, ap);
    1.69 +	va_end(ap);
    1.70 +
    1.71 +	m_ParseError.assign(buffer);
    1.72 +}
    1.73 +
    1.74 +void CPhraseFile::ParseWarning(const char *message, ...)
    1.75 +{
    1.76 +	char buffer[1024];
    1.77 +	va_list ap;
    1.78 +	va_start(ap, message);
    1.79 +	vsnprintf(buffer, sizeof(buffer), message, ap);
    1.80 +	va_end(ap);
    1.81 +
    1.82 +	if (!m_FileLogged)
    1.83 +	{
    1.84 +		g_Logger.LogError("[SM] Warning(s) encountered in translation file \"%s\"", m_File.c_str());
    1.85 +		m_FileLogged = true;
    1.86 +	}
    1.87 +
    1.88 +	g_Logger.LogError("[SM] %s", message);
    1.89 +}
    1.90 +
    1.91 +void CPhraseFile::ReparseFile()
    1.92 +{
    1.93 +	if (m_pPhraseLookup)
    1.94 +	{
    1.95 +		sm_trie_destroy(m_pPhraseLookup);
    1.96 +	}
    1.97 +	m_pPhraseLookup = sm_trie_create();
    1.98 +
    1.99 +	m_LangCount = m_pTranslator->GetLanguageCount();
   1.100 +
   1.101 +	if (!m_LangCount)
   1.102 +	{
   1.103 +		return;
   1.104 +	}
   1.105 +
   1.106 +	SMCParseError err;
   1.107 +	char path[PLATFORM_MAX_PATH+1];
   1.108 +	g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "translations/%s", m_File.c_str());
   1.109 +	unsigned int line=0, col=0;
   1.110 +
   1.111 +	if ((err=g_TextParser.ParseFile_SMC(path, this, &line, &col)) != SMCParse_Okay)
   1.112 +	{
   1.113 +		const char *msg = g_TextParser.GetSMCErrorString(err);
   1.114 +		if (!msg)
   1.115 +		{
   1.116 +			msg = m_ParseError.c_str();
   1.117 +		}
   1.118 +
   1.119 +		g_Logger.LogError("[SM] Fatal error encountered parsing translation file \"%s\"", m_File.c_str());
   1.120 +		g_Logger.LogError("[SM] Error (line %d, column %d): %s", line, col, msg);
   1.121 +	}
   1.122 +}
   1.123 +
   1.124 +void CPhraseFile::ReadSMC_ParseStart()
   1.125 +{
   1.126 +	m_CurPhrase = -1;
   1.127 +	m_ParseState = PPS_None;
   1.128 +	m_CurLine = 0;
   1.129 +	m_FileLogged = false;
   1.130 +	m_LastPhraseString.clear();
   1.131 +}
   1.132 +
   1.133 +SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline)
   1.134 +{
   1.135 +	m_CurLine = curline;
   1.136 +
   1.137 +	return SMCParse_Continue;
   1.138 +}
   1.139 +
   1.140 +SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes)
   1.141 +{
   1.142 +	bool recognized = false;
   1.143 +	if (m_ParseState == PPS_None)
   1.144 +	{
   1.145 +		if (strcmp(name, "Phrases") == 0)
   1.146 +		{
   1.147 +			m_ParseState = PPS_Phrases;
   1.148 +			recognized = true;
   1.149 +		}
   1.150 +	} else if (m_ParseState == PPS_Phrases) {
   1.151 +		m_ParseState = PPS_InPhrase;
   1.152 +		recognized = true;
   1.153 +
   1.154 +		phrase_t *pPhrase;
   1.155 +		m_CurPhrase = m_pMemory->CreateMem(sizeof(phrase_t), (void **)&pPhrase);
   1.156 +
   1.157 +		/* Create the reverse lookup */
   1.158 +		if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase)))
   1.159 +		{
   1.160 +			ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, m_CurLine);
   1.161 +			/* :IDEA: prevent memory waste by seeking backwards in memtable? */
   1.162 +			m_CurPhrase = -1;
   1.163 +		} else {
   1.164 +			/* Initialize new phrase */
   1.165 +			trans_t *pTrans;
   1.166 +	
   1.167 +			pPhrase->fmt_count = 0;
   1.168 +			pPhrase->fmt_list = -1;
   1.169 +			pPhrase->trans_tbl = m_pMemory->CreateMem(sizeof(trans_t) * m_LangCount, (void **)&pTrans);
   1.170 +			pPhrase->translations = 0;
   1.171 +			pPhrase->fmt_bytes = 0;
   1.172 +	
   1.173 +			for (unsigned int i=0; i<m_LangCount; i++)
   1.174 +			{
   1.175 +				pTrans[i].stridx = -1;
   1.176 +			}
   1.177 +			m_LastPhraseString.assign(name);
   1.178 +		}
   1.179 +	} else if (m_ParseState == PPS_InPhrase) {
   1.180 +		ParseError("Phrase sections may not have sub-sections");
   1.181 +		return SMCParse_HaltFail;
   1.182 +	}
   1.183 +
   1.184 +	if (!recognized)
   1.185 +	{
   1.186 +		ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, m_CurLine);
   1.187 +	}
   1.188 +
   1.189 +	return SMCParse_Continue;
   1.190 +}
   1.191 +
   1.192 +SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
   1.193 +{
   1.194 +	/* See if we are ignoring a phrase */
   1.195 +	if (!m_CurPhrase)
   1.196 +	{
   1.197 +		return SMCParse_Continue;
   1.198 +	}
   1.199 +
   1.200 +	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.201 +	
   1.202 +	if (key[0] == '#' && strcmp(key, "#format") == 0)
   1.203 +	{
   1.204 +		if (pPhrase->fmt_list != -1)
   1.205 +		{
   1.206 +			ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine);
   1.207 +			return SMCParse_Continue;
   1.208 +		}
   1.209 +
   1.210 +		if (pPhrase->translations > 0)
   1.211 +		{
   1.212 +			ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine);
   1.213 +			return SMCParse_Continue;
   1.214 +		}
   1.215 +
   1.216 +		/* Do an initial browsing for error checking and what not */
   1.217 +		enum ParseStates
   1.218 +		{
   1.219 +			Parse_None,
   1.220 +			Parse_Index,
   1.221 +			Parse_Format,
   1.222 +		};
   1.223 +
   1.224 +		ParseStates state = Parse_None;
   1.225 +
   1.226 +		const char *old_value = value;
   1.227 +		while (*value != '\0')
   1.228 +		{
   1.229 +			if (state == Parse_None)
   1.230 +			{
   1.231 +				if (*value == '{')
   1.232 +				{
   1.233 +					pPhrase->fmt_count++;
   1.234 +					state = Parse_Index;
   1.235 +				} else if (*value == ',') {
   1.236 +					/* Do nothing */
   1.237 +				} else {
   1.238 +					unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
   1.239 +					if (bytes != 1 || !isalpha(*value))
   1.240 +					{
   1.241 +						ParseWarning("Invalid token '%c' in #format property on line %d.", *value, m_CurLine);
   1.242 +					}
   1.243 +				}
   1.244 +			} else if (state == Parse_Index) {
   1.245 +				if (*value == ':')
   1.246 +				{
   1.247 +					state = Parse_Format;
   1.248 +					if (value - old_value >= 15)
   1.249 +					{
   1.250 +						ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", m_CurLine);
   1.251 +						m_CurPhrase = -1;
   1.252 +						return SMCParse_Continue;
   1.253 +					}
   1.254 +				} else {
   1.255 +					unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
   1.256 +					if (bytes != 1 || !isdigit(*value))
   1.257 +					{
   1.258 +						ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
   1.259 +									*value,
   1.260 +									m_CurLine);
   1.261 +						m_CurPhrase = -1;
   1.262 +						return SMCParse_Continue;
   1.263 +					}
   1.264 +				}
   1.265 +			} else if (state == Parse_Format) {
   1.266 +				if (*value == '}')
   1.267 +				{
   1.268 +					state = Parse_None;
   1.269 +				}
   1.270 +			}
   1.271 +			value++;
   1.272 +		}
   1.273 +
   1.274 +		if (state != Parse_None)
   1.275 +		{
   1.276 +			/* Moose clam cow. */
   1.277 +			ParseWarning("Unterminated format string on line %d, phrase will be ignored.", m_CurLine);
   1.278 +			m_CurPhrase = -1;
   1.279 +			return SMCParse_Continue;
   1.280 +		}
   1.281 +		value = old_value;
   1.282 +
   1.283 +		/* Allocate the format table */
   1.284 +		char fmt_buf[16];
   1.285 +		int *fmt_list;
   1.286 +		pPhrase->fmt_list = m_pMemory->CreateMem(sizeof(int) * pPhrase->fmt_count, (void **)&fmt_list);
   1.287 +
   1.288 +		/* Initialize */
   1.289 +		for (size_t i=0; i<pPhrase->fmt_count; i++)
   1.290 +		{
   1.291 +			fmt_list[i] = -1;
   1.292 +		}
   1.293 +
   1.294 +		/* Now we need to read again... this time into the format buffer */
   1.295 +		const char *in_ptr = value;
   1.296 +		const char *idx_ptr = NULL;
   1.297 +		char *out_ptr = NULL;
   1.298 +		unsigned int cur_idx = 0;
   1.299 +		state = Parse_None;
   1.300 +		while (*in_ptr != '\0')
   1.301 +		{
   1.302 +			if (state == Parse_None)
   1.303 +			{
   1.304 +				if (*in_ptr == '{')
   1.305 +				{
   1.306 +					state = Parse_Index;
   1.307 +					idx_ptr = NULL;
   1.308 +				}
   1.309 +			} else if (state == Parse_Index) {
   1.310 +				if (*in_ptr == ':')
   1.311 +				{
   1.312 +					/* Check the number! */
   1.313 +					if (!idx_ptr)
   1.314 +					{
   1.315 +						ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", m_CurLine);
   1.316 +						m_CurPhrase = -1;
   1.317 +						return SMCParse_Continue;
   1.318 +					}
   1.319 +					long idx = strtol(idx_ptr, NULL, 10);
   1.320 +					if (idx < 1 || idx > (long)pPhrase->fmt_count)
   1.321 +					{
   1.322 +						ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
   1.323 +						m_CurPhrase = -1;
   1.324 +						return SMCParse_Continue;
   1.325 +					} else if (fmt_list[idx - 1] != -1) {
   1.326 +						ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
   1.327 +						m_CurPhrase = -1;
   1.328 +						return SMCParse_Continue;
   1.329 +					}
   1.330 +					cur_idx = (unsigned int)idx;
   1.331 +					state = Parse_Format;
   1.332 +					out_ptr = NULL;
   1.333 +				} else if (!idx_ptr) {
   1.334 +					idx_ptr = in_ptr;
   1.335 +				}
   1.336 +			} else if (state == Parse_Format) {
   1.337 +				if (*in_ptr == '}')
   1.338 +				{
   1.339 +					if (!out_ptr)
   1.340 +					{
   1.341 +						ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", m_CurLine);
   1.342 +						m_CurPhrase = -1;
   1.343 +						return SMCParse_Continue;
   1.344 +					}
   1.345 +					*out_ptr = '\0';
   1.346 +					state = Parse_None;
   1.347 +					/* Now, add this to our table */
   1.348 +					fmt_list[cur_idx - 1] = m_pStringTab->AddString(fmt_buf);
   1.349 +					pPhrase->fmt_bytes += strlen(fmt_buf);
   1.350 +				} else {
   1.351 +					if (!out_ptr)
   1.352 +					{
   1.353 +						out_ptr = fmt_buf;
   1.354 +						*out_ptr++ = '%';
   1.355 +					}
   1.356 +					/* Check length ... */
   1.357 +					if ((unsigned)(out_ptr - fmt_buf) >= sizeof(fmt_buf) - 1)
   1.358 +					{
   1.359 +						ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
   1.360 +									 m_CurLine);
   1.361 +						m_CurPhrase = -1;
   1.362 +						return SMCParse_Continue;
   1.363 +					}
   1.364 +					*out_ptr++ = *in_ptr;
   1.365 +				}
   1.366 +			}
   1.367 +			in_ptr++;
   1.368 +		}
   1.369 +
   1.370 +		/* If we've gotten here, we only accepted unique indexes in a range.
   1.371 +		 * Therefore, the list has to be completely filled.  Double check anyway.
   1.372 +		 */
   1.373 +		for (size_t i=0; i<pPhrase->fmt_count; i++)
   1.374 +		{
   1.375 +			if (fmt_list[i] == -1)
   1.376 +			{
   1.377 +				ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
   1.378 +							 i + 1,
   1.379 +							 m_CurLine);
   1.380 +				m_CurPhrase = -1;
   1.381 +				return SMCParse_Continue;
   1.382 +			}
   1.383 +		}
   1.384 +	} else {
   1.385 +		size_t len = strlen(key);
   1.386 +		if (len != 2)
   1.387 +		{
   1.388 +			ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine);
   1.389 +			return SMCParse_Continue;
   1.390 +		}
   1.391 +
   1.392 +		unsigned int lang;
   1.393 +		if (!m_pTranslator->GetLanguageByCode(key, &lang))
   1.394 +		{
   1.395 +			/* Ignore if we don't have a language.
   1.396 +			 * :IDEA: issue a one-time alert?
   1.397 +			 */
   1.398 +			return SMCParse_Continue;
   1.399 +		}
   1.400 +
   1.401 +		/* See how many bytes we need for this string, then allocate.
   1.402 +		 * NOTE: THIS SHOULD GUARANTEE THAT WE DO NOT NEED TO NEED TO SIZE CHECK 
   1.403 +		 */
   1.404 +		len = strlen(value) + pPhrase->fmt_bytes + 1;
   1.405 +		char *out_buf;
   1.406 +		int out_idx;
   1.407 +
   1.408 +		out_idx = m_pMemory->CreateMem(len, (void **)&out_buf);
   1.409 +
   1.410 +		int *fmt_order;
   1.411 +		int *fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
   1.412 +		trans_t *pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
   1.413 +
   1.414 +		pTrans = &pTrans[lang];
   1.415 +		pTrans->stridx = out_idx;
   1.416 +
   1.417 +		bool params[MAX_TRANSLATE_PARAMS];
   1.418 +
   1.419 +		/* Build the format order list, if necessary */
   1.420 +		if (fmt_list)
   1.421 +		{
   1.422 +			pTrans->fmt_order = m_pMemory->CreateMem(pPhrase->fmt_count * sizeof(int), (void **)&fmt_order);
   1.423 +
   1.424 +			for (unsigned int i=0; i<pPhrase->fmt_count; i++)
   1.425 +			{
   1.426 +				fmt_order[i] = -1;
   1.427 +			}
   1.428 +			memset(&params[0], 0, sizeof(params));
   1.429 +		}
   1.430 +
   1.431 +		const char *in_ptr = value;
   1.432 +		char *out_ptr = out_buf;
   1.433 +		unsigned int order_idx = 0;
   1.434 +		while (*in_ptr != '\0')
   1.435 +		{
   1.436 +			if (*in_ptr == '\\')
   1.437 +			{
   1.438 +				switch (*(in_ptr + 1))
   1.439 +				{
   1.440 +				case '\n':
   1.441 +					*out_ptr++ = '\n';
   1.442 +					break;
   1.443 +				case '\t':
   1.444 +					*out_ptr++ = '\t';
   1.445 +					break;
   1.446 +				case '\r':
   1.447 +					*out_ptr++ = '\r';
   1.448 +					break;
   1.449 +				case '{':
   1.450 +					*out_ptr++ = '{';
   1.451 +					break;
   1.452 +				default:
   1.453 +					/* Copy both bytes since we don't know what's going on */
   1.454 +					*out_ptr++ = *in_ptr++;
   1.455 +					*out_ptr++ = *in_ptr;
   1.456 +					break;
   1.457 +				}
   1.458 +				/* Skip past the last byte read */
   1.459 +				in_ptr++;
   1.460 +			} else if (*in_ptr == '{' && fmt_list != NULL) {
   1.461 +				/* Search for parameters if this is a formatted string */
   1.462 +				const char *scrap_in_point = in_ptr;
   1.463 +				const char *digit_start = ++in_ptr;
   1.464 +				unsigned int bytes;
   1.465 +				while (*in_ptr != '\0')
   1.466 +				{
   1.467 +					bytes = g_TextParser.GetUTF8CharBytes(in_ptr);
   1.468 +					if (bytes != 1)
   1.469 +					{
   1.470 +						goto scrap_point;
   1.471 +					}
   1.472 +					if (*in_ptr == '}')
   1.473 +					{
   1.474 +						/* Did we get an index? */
   1.475 +						if (in_ptr == digit_start)
   1.476 +						{
   1.477 +							goto scrap_point;
   1.478 +						}
   1.479 +						/* Is it valid? */
   1.480 +						long idx = strtol(digit_start, NULL, 10);
   1.481 +						if (idx < 1 || idx > (int)pPhrase->fmt_count)
   1.482 +						{
   1.483 +							goto scrap_point;
   1.484 +						}
   1.485 +						if (params[idx-1])
   1.486 +						{
   1.487 +							goto scrap_point;
   1.488 +						}
   1.489 +
   1.490 +						/* We're safe to insert the string.  First mark order. */
   1.491 +						fmt_order[order_idx++] = (int)idx - 1;
   1.492 +						/* Now concatenate */
   1.493 +						out_ptr += sprintf(out_ptr, "%s", m_pStringTab->GetString(fmt_list[idx-1]));
   1.494 +						/* Mark as used */
   1.495 +						params[idx-1] = true;
   1.496 +
   1.497 +						goto cont_loop;
   1.498 +					}
   1.499 +					in_ptr++;
   1.500 +				}
   1.501 +scrap_point:
   1.502 +				/* Pretend none of this ever happened.  Move along now! */
   1.503 +				in_ptr = scrap_in_point;
   1.504 +				*out_ptr++ = *in_ptr;
   1.505 +			} else {
   1.506 +				*out_ptr++ = *in_ptr;
   1.507 +			}
   1.508 +cont_loop:
   1.509 +			in_ptr++;
   1.510 +		}
   1.511 +		*out_ptr = '\0';
   1.512 +		pPhrase->translations++;
   1.513 +	}
   1.514 +
   1.515 +	return SMCParse_Continue;
   1.516 +}
   1.517 +
   1.518 +SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
   1.519 +{
   1.520 +	if (m_ParseState == PPS_InPhrase)
   1.521 +	{
   1.522 +		if (m_CurPhrase == -1 && m_LastPhraseString.size())
   1.523 +		{
   1.524 +			sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
   1.525 +		}
   1.526 +		m_CurPhrase = -1;
   1.527 +		m_ParseState = PPS_Phrases;
   1.528 +		m_LastPhraseString.assign("");
   1.529 +	} else if (m_ParseState == PPS_Phrases) {
   1.530 +		m_ParseState = PPS_None;
   1.531 +	}
   1.532 +
   1.533 +	return SMCParse_Continue;
   1.534 +}
   1.535 +
   1.536 +void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed)
   1.537 +{
   1.538 +	/* Check to see if we have any dangling phrases that weren't completed, and scrap them */
   1.539 +	if ((halted || failed) && m_LastPhraseString.size())
   1.540 +	{
   1.541 +		sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
   1.542 +	}
   1.543 +}
   1.544 +
   1.545 +TransError CPhraseFile::GetTranslation(const char *szPhrase, unsigned int lang_id, Translation *pTrans)
   1.546 +{
   1.547 +	if (lang_id >= m_LangCount)
   1.548 +	{
   1.549 +		return Trans_BadLanguage;
   1.550 +	}
   1.551 +
   1.552 +	void *object;
   1.553 +	if (!sm_trie_retrieve(m_pPhraseLookup, szPhrase, &object))
   1.554 +	{
   1.555 +		return Trans_BadPhrase;
   1.556 +	}
   1.557 +
   1.558 +	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(reinterpret_cast<int>(object));
   1.559 +	trans_t *trans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
   1.560 +
   1.561 +	trans = &trans[lang_id];
   1.562 +
   1.563 +	if (trans->stridx == -1)
   1.564 +	{
   1.565 +		return Trans_BadPhraseLanguage;
   1.566 +	}
   1.567 +
   1.568 +	pTrans->fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order);
   1.569 +	pTrans->fmt_count = pPhrase->fmt_count;
   1.570 +
   1.571 +	pTrans->szPhrase = m_pStringTab->GetString(trans->stridx);
   1.572 +
   1.573 +	return Trans_Okay;
   1.574 +}
   1.575 +
   1.576 +const char *CPhraseFile::GetFilename()
   1.577 +{
   1.578 +	return m_File.c_str();
   1.579 +}
   1.580 +
   1.581 +/**************************
   1.582 + ** MAIN TRANSLATOR CODE **
   1.583 + **************************/
   1.584 +
   1.585 +CTranslator::CTranslator()
   1.586 +{
   1.587 +	m_pStringTab = new BaseStringTable(2048);
   1.588 +	m_pLCodeLookup = sm_trie_create();
   1.589 +}
   1.590 +
   1.591 +CTranslator::~CTranslator()
   1.592 +{
   1.593 +	for (size_t i=0; i<m_Files.size(); i++)
   1.594 +	{
   1.595 +		delete m_Files[i];
   1.596 +	}
   1.597 +
   1.598 +	for (size_t i=0; i<m_Languages.size(); i++)
   1.599 +	{
   1.600 +		delete m_Languages[i];
   1.601 +	}
   1.602 +
   1.603 +	sm_trie_destroy(m_pLCodeLookup);
   1.604 +
   1.605 +	delete m_pStringTab;
   1.606 +}
   1.607 +
   1.608 +void CTranslator::OnSourceModAllInitialized()
   1.609 +{
   1.610 +	AddLanguage("en", "English");
   1.611 +	unsigned int id = FindOrAddPhraseFile("core.cfg");
   1.612 +	g_pCorePhrases = GetFileByIndex(id);
   1.613 +}
   1.614 +
   1.615 +bool CTranslator::GetLanguageByCode(const char *code, unsigned int *index)
   1.616 +{
   1.617 +	void *_index;
   1.618 +
   1.619 +	if (!sm_trie_retrieve(m_pLCodeLookup, code, &_index))
   1.620 +	{
   1.621 +		return false;
   1.622 +	}
   1.623 +
   1.624 +	if (index)
   1.625 +	{
   1.626 +		*index = reinterpret_cast<unsigned int>(_index);
   1.627 +	}
   1.628 +
   1.629 +	return true;
   1.630 +}
   1.631 +
   1.632 +unsigned int CTranslator::GetLanguageCount()
   1.633 +{
   1.634 +	return (unsigned int)m_Languages.size();
   1.635 +}
   1.636 +
   1.637 +BaseStringTable *CTranslator::GetStringTable()
   1.638 +{
   1.639 +	return m_pStringTab;
   1.640 +}
   1.641 +
   1.642 +unsigned int CTranslator::FindOrAddPhraseFile(const char *phrase_file)
   1.643 +{
   1.644 +	for (size_t i=0; i<m_Files.size(); i++)
   1.645 +	{
   1.646 +		if (strcmp(m_Files[i]->GetFilename(), phrase_file) == 0)
   1.647 +		{
   1.648 +			return (unsigned int)i;
   1.649 +		}
   1.650 +	}
   1.651 +
   1.652 +	CPhraseFile *pFile = new CPhraseFile(this, phrase_file);
   1.653 +	unsigned int idx = (unsigned int)m_Files.size();
   1.654 +	
   1.655 +	m_Files.push_back(pFile);
   1.656 +
   1.657 +	pFile->ReparseFile();
   1.658 +
   1.659 +	return idx;
   1.660 +}
   1.661 +
   1.662 +void CTranslator::RebuildLanguageDatabase(const char *lang_header_file)
   1.663 +{
   1.664 +	/* Erase everything we have */
   1.665 +	sm_trie_destroy(m_pLCodeLookup);
   1.666 +	m_pLCodeLookup = sm_trie_create();
   1.667 +	m_pStringTab->Reset();
   1.668 +
   1.669 +	for (size_t i=0; i<m_Languages.size(); i++)
   1.670 +	{
   1.671 +		delete m_Languages[i];
   1.672 +	}
   1.673 +	m_Languages.clear();
   1.674 +
   1.675 +	/* Start anew */
   1.676 +	SMCParseError err;
   1.677 +	unsigned int line=0, col=0;
   1.678 +	if ((err=g_TextParser.ParseFile_SMC(lang_header_file, this, &line, &col)) != SMCParse_Okay)
   1.679 +	{
   1.680 +		const char *str_err = g_TextParser.GetSMCErrorString(err);
   1.681 +		if (!str_err)
   1.682 +		{
   1.683 +			str_err = m_CustomError.c_str();
   1.684 +		}
   1.685 +
   1.686 +		g_Logger.LogError("[SM] Failed to parse language header file: \"%s\"", lang_header_file);
   1.687 +		g_Logger.LogError("[SM] Parse error (line %d, column %d): %s", line, col, str_err);
   1.688 +	}
   1.689 +
   1.690 +	if (!m_Languages.size())
   1.691 +	{
   1.692 +		g_Logger.LogError("[SM] Fatal error, no languages found! Translation will not work.");
   1.693 +	}
   1.694 +
   1.695 +	for (size_t i=0; i<m_Files.size(); i++)
   1.696 +	{
   1.697 +		m_Files[i]->ReparseFile();
   1.698 +	}
   1.699 +}
   1.700 +
   1.701 +void CTranslator::ReadSMC_ParseStart()
   1.702 +{
   1.703 +	m_InLanguageSection = false;
   1.704 +	m_CustomError.clear();
   1.705 +}
   1.706 +
   1.707 +SMCParseResult CTranslator::ReadSMC_NewSection(const char *name, bool opt_quotes)
   1.708 +{
   1.709 +	if (!m_InLanguageSection)
   1.710 +	{
   1.711 +		if (strcmp(name, "Languages") == 0)
   1.712 +		{
   1.713 +			m_InLanguageSection = true;
   1.714 +		}
   1.715 +	}
   1.716 +
   1.717 +	if (!m_InLanguageSection)
   1.718 +	{
   1.719 +		g_Logger.LogError("[SM] Warning: Unrecognized section \"%s\" in languages.cfg", name);
   1.720 +	}
   1.721 +
   1.722 +	return SMCParse_Continue;
   1.723 +}
   1.724 +
   1.725 +SMCParseResult CTranslator::ReadSMC_LeavingSection()
   1.726 +{
   1.727 +	m_InLanguageSection = false;
   1.728 +
   1.729 +	return SMCParse_Continue;
   1.730 +}
   1.731 +
   1.732 +SMCParseResult CTranslator::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
   1.733 +{
   1.734 +	size_t len = strlen(key);
   1.735 +
   1.736 +	if (len != 2)
   1.737 +	{
   1.738 +		g_Logger.LogError("[SM] Warning encountered parsing languages.cfg file.");
   1.739 +		g_Logger.LogError("[SM] Invalid language code \"%s\" is being ignored.", key);
   1.740 +	}
   1.741 +
   1.742 +	AddLanguage(key, value);
   1.743 +
   1.744 +	return SMCParse_Continue;
   1.745 +}
   1.746 +
   1.747 +bool CTranslator::AddLanguage(const char *langcode, const char *description)
   1.748 +{
   1.749 +	if (sm_trie_retrieve(m_pLCodeLookup, langcode, NULL))
   1.750 +	{
   1.751 +		return false;
   1.752 +	}
   1.753 +
   1.754 +	Language *pLanguage = new Language;
   1.755 +	unsigned int idx = m_Languages.size();
   1.756 +
   1.757 +	pLanguage->m_code2[0] = langcode[0];
   1.758 +	pLanguage->m_code2[1] = langcode[1];
   1.759 +	pLanguage->m_code2[2] = langcode[2];
   1.760 +	pLanguage->m_FullName = m_pStringTab->AddString(description);
   1.761 +
   1.762 +	sm_trie_insert(m_pLCodeLookup, langcode, reinterpret_cast<void *>(idx));
   1.763 +
   1.764 +	m_Languages.push_back(pLanguage);
   1.765 +
   1.766 +	return true;
   1.767 +}
   1.768 +
   1.769 +CPhraseFile *CTranslator::GetFileByIndex(unsigned int index)
   1.770 +{
   1.771 +	if (index >= m_Files.size())
   1.772 +	{
   1.773 +		return NULL;
   1.774 +	}
   1.775 +
   1.776 +	return m_Files[index];
   1.777 +}
   1.778 +
   1.779 +size_t CTranslator::Translate(char *buffer, size_t maxlength, void **params, const Translation *pTrans)
   1.780 +{
   1.781 +	void *new_params[MAX_TRANSLATE_PARAMS];
   1.782 +
   1.783 +	/* Rewrite the parameter order */
   1.784 +	for (unsigned int i=0; i<pTrans->fmt_count; i++)
   1.785 +	{
   1.786 +		new_params[i] = params[pTrans->fmt_order[i]];
   1.787 +	}
   1.788 +
   1.789 +	return gnprintf(buffer, maxlength, pTrans->szPhrase, new_params);
   1.790 +}
   1.791 +
   1.792 +TransError CTranslator::CoreTrans(int client, 
   1.793 +								  char *buffer, 
   1.794 +								  size_t maxlength, 
   1.795 +								  const char *phrase, 
   1.796 +								  void **params, 
   1.797 +								  size_t *outlen)
   1.798 +{
   1.799 +	/* :TODO: do language stuff here */
   1.800 +
   1.801 +	if (!g_pCorePhrases)
   1.802 +	{
   1.803 +		return Trans_BadPhraseFile;
   1.804 +	}
   1.805 +
   1.806 +	Translation trans;
   1.807 +	TransError err;
   1.808 +	if ((err=g_pCorePhrases->GetTranslation(phrase, 0, &trans)) != Trans_Okay)
   1.809 +	{
   1.810 +		return err;
   1.811 +	}
   1.812 +
   1.813 +	size_t len = Translate(buffer, maxlength, params, &trans);
   1.814 +
   1.815 +	if (outlen)
   1.816 +	{
   1.817 +		*outlen = len;
   1.818 +	}
   1.819 +
   1.820 +	return Trans_Okay;
   1.821 +}