core/Translator.cpp
branchrefac-jit
changeset 2284 026da5437111
parent 2192 7602d7fb60d9
child 2288 15c77b21589a
     1.1 --- a/core/Translator.cpp	Fri Jul 11 06:51:21 2008 +0000
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,1076 +0,0 @@
     1.4 -/**
     1.5 - * vim: set ts=4 :
     1.6 - * =============================================================================
     1.7 - * SourceMod
     1.8 - * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     1.9 - * =============================================================================
    1.10 - *
    1.11 - * This program is free software; you can redistribute it and/or modify it under
    1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
    1.13 - * Free Software Foundation.
    1.14 - * 
    1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
    1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    1.17 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    1.18 - * details.
    1.19 - *
    1.20 - * You should have received a copy of the GNU General Public License along with
    1.21 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    1.22 - *
    1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
    1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    1.26 - * by the Valve Corporation.  You must obey the GNU General Public License in
    1.27 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    1.28 - * this exception to all derivative works.  AlliedModders LLC defines further
    1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    1.30 - * or <http://www.sourcemod.net/license.php>.
    1.31 - *
    1.32 - * Version: $Id$
    1.33 - */
    1.34 -
    1.35 -#include <stdarg.h>
    1.36 -#include <stdlib.h>
    1.37 -#include <ctype.h>
    1.38 -#include "Translator.h"
    1.39 -#include "Logger.h"
    1.40 -#include "LibrarySys.h"
    1.41 -#include "sm_stringutil.h"
    1.42 -#include "sourcemod.h"
    1.43 -#include "PlayerManager.h"
    1.44 -#include "PhraseCollection.h"
    1.45 -#include "ShareSys.h"
    1.46 -
    1.47 -Translator g_Translator;
    1.48 -IPhraseCollection *g_pCorePhrases = NULL;
    1.49 -unsigned int g_pCorePhraseID = 0;
    1.50 -
    1.51 -struct trans_t
    1.52 -{
    1.53 -	int stridx;
    1.54 -	int fmt_order;
    1.55 -};
    1.56 -
    1.57 -struct phrase_t
    1.58 -{
    1.59 -	int fmt_list;
    1.60 -	unsigned int fmt_count;
    1.61 -	unsigned int fmt_bytes;
    1.62 -	int trans_tbl;
    1.63 -	unsigned int translations;
    1.64 -};
    1.65 -
    1.66 -CPhraseFile::CPhraseFile(Translator *pTranslator, const char *file)
    1.67 -{
    1.68 -	m_pStringTab = pTranslator->GetStringTable();
    1.69 -	m_pMemory = m_pStringTab->GetMemTable();
    1.70 -	m_LangCount = pTranslator->GetLanguageCount();
    1.71 -	m_File.assign(file);
    1.72 -	m_pTranslator = pTranslator;
    1.73 -	m_pPhraseLookup = NULL;
    1.74 -}
    1.75 -
    1.76 -CPhraseFile::~CPhraseFile()
    1.77 -{
    1.78 -	if (m_pPhraseLookup)
    1.79 -	{
    1.80 -		sm_trie_destroy(m_pPhraseLookup);
    1.81 -	}
    1.82 -}
    1.83 -
    1.84 -void CPhraseFile::ParseError(const char *message, ...)
    1.85 -{
    1.86 -	char buffer[1024];
    1.87 -	va_list ap;
    1.88 -	va_start(ap, message);
    1.89 -	vsnprintf(buffer, sizeof(buffer), message, ap);
    1.90 -	va_end(ap);
    1.91 -
    1.92 -	m_ParseError.assign(buffer);
    1.93 -}
    1.94 -
    1.95 -void CPhraseFile::ParseWarning(const char *message, ...)
    1.96 -{
    1.97 -	char buffer[1024];
    1.98 -	va_list ap;
    1.99 -	va_start(ap, message);
   1.100 -	vsnprintf(buffer, sizeof(buffer), message, ap);
   1.101 -	va_end(ap);
   1.102 -
   1.103 -	if (!m_FileLogged)
   1.104 -	{
   1.105 -		g_Logger.LogError("[SM] Warning(s) encountered in translation file \"%s\"", m_File.c_str());
   1.106 -		m_FileLogged = true;
   1.107 -	}
   1.108 -
   1.109 -	g_Logger.LogError("[SM] %s", message);
   1.110 -}
   1.111 -
   1.112 -void CPhraseFile::ReparseFile()
   1.113 -{
   1.114 -	if (m_pPhraseLookup)
   1.115 -	{
   1.116 -		sm_trie_destroy(m_pPhraseLookup);
   1.117 -	}
   1.118 -	m_pPhraseLookup = sm_trie_create();
   1.119 -
   1.120 -	m_LangCount = m_pTranslator->GetLanguageCount();
   1.121 -
   1.122 -	if (!m_LangCount)
   1.123 -	{
   1.124 -		return;
   1.125 -	}
   1.126 -
   1.127 -	SMCError err;
   1.128 -	char path[PLATFORM_MAX_PATH];
   1.129 -	g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "translations/%s", m_File.c_str());
   1.130 -
   1.131 -	//backwards compatibility shim
   1.132 -	/* :HACKHACK: Change .cfg/.txt and vice versa for compatibility */
   1.133 -	if (!g_LibSys.PathExists(path))
   1.134 -	{
   1.135 -		if (m_File.compare("common.cfg") == 0)
   1.136 -		{
   1.137 -			UTIL_ReplaceAll(path, sizeof(path), "common.cfg", "common.phrases.txt");
   1.138 -		} else if (strstr(path, ".cfg")) {
   1.139 -			UTIL_ReplaceAll(path, sizeof(path), ".cfg", ".txt");
   1.140 -		} else if (strstr(path, ".txt")) {
   1.141 -			UTIL_ReplaceAll(path, sizeof(path), ".txt", ".cfg");
   1.142 -		}
   1.143 -	}
   1.144 -
   1.145 -	SMCStates states;
   1.146 -
   1.147 -	if ((err=textparsers->ParseFile_SMC(path, this, &states)) != SMCError_Okay)
   1.148 -	{
   1.149 -		const char *msg = textparsers->GetSMCErrorString(err);
   1.150 -		if (!msg)
   1.151 -		{
   1.152 -			msg = m_ParseError.c_str();
   1.153 -		}
   1.154 -
   1.155 -		g_Logger.LogError("[SM] Fatal error encountered parsing translation file \"%s\"", m_File.c_str());
   1.156 -		g_Logger.LogError("[SM] Error (line %d, column %d): %s", states.line, states.col, msg);
   1.157 -	}
   1.158 -}
   1.159 -
   1.160 -void CPhraseFile::ReadSMC_ParseStart()
   1.161 -{
   1.162 -	m_CurPhrase = -1;
   1.163 -	m_ParseState = PPS_None;
   1.164 -	m_FileLogged = false;
   1.165 -	m_LastPhraseString.clear();
   1.166 -}
   1.167 -
   1.168 -SMCResult CPhraseFile::ReadSMC_NewSection(const SMCStates *states, const char *name)
   1.169 -{
   1.170 -	bool recognized = false;
   1.171 -	if (m_ParseState == PPS_None)
   1.172 -	{
   1.173 -		if (strcmp(name, "Phrases") == 0)
   1.174 -		{
   1.175 -			m_ParseState = PPS_Phrases;
   1.176 -			recognized = true;
   1.177 -		}
   1.178 -	} 
   1.179 -	else if (m_ParseState == PPS_Phrases) 
   1.180 -	{
   1.181 -		m_ParseState = PPS_InPhrase;
   1.182 -		recognized = true;
   1.183 -
   1.184 -		phrase_t *pPhrase;
   1.185 -		m_CurPhrase = m_pMemory->CreateMem(sizeof(phrase_t), (void **)&pPhrase);
   1.186 -
   1.187 -		/* Create the reverse lookup */
   1.188 -		if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase)))
   1.189 -		{
   1.190 -			ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, states->line);
   1.191 -			/* :IDEA: prevent memory waste by seeking backwards in memtable? */
   1.192 -			m_CurPhrase = -1;
   1.193 -		} 
   1.194 -		else 
   1.195 -		{
   1.196 -			/* Initialize new phrase */
   1.197 -			trans_t *pTrans;
   1.198 -	
   1.199 -			pPhrase->fmt_count = 0;
   1.200 -			pPhrase->fmt_list = -1;
   1.201 -
   1.202 -			int trans_tbl = m_pMemory->CreateMem(sizeof(trans_t) * m_LangCount, (void **)&pTrans);
   1.203 -			pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.204 -			pPhrase->trans_tbl = trans_tbl;
   1.205 -
   1.206 -			pPhrase->translations = 0;
   1.207 -			pPhrase->fmt_bytes = 0;
   1.208 -	
   1.209 -			for (unsigned int i=0; i<m_LangCount; i++)
   1.210 -			{
   1.211 -				pTrans[i].stridx = -1;
   1.212 -			}
   1.213 -			m_LastPhraseString.assign(name);
   1.214 -		}
   1.215 -	} 
   1.216 -	else if (m_ParseState == PPS_InPhrase) 
   1.217 -	{
   1.218 -		ParseError("Phrase sections may not have sub-sections");
   1.219 -		return SMCResult_HaltFail;
   1.220 -	}
   1.221 -
   1.222 -	if (!recognized)
   1.223 -	{
   1.224 -		ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, states->line);
   1.225 -	}
   1.226 -
   1.227 -	return SMCResult_Continue;
   1.228 -}
   1.229 -
   1.230 -SMCResult CPhraseFile::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   1.231 -{
   1.232 -	/* See if we are ignoring a phrase */
   1.233 -	if (m_CurPhrase == -1)
   1.234 -	{
   1.235 -		return SMCResult_Continue;
   1.236 -	}
   1.237 -
   1.238 -	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.239 -	
   1.240 -	if (key[0] == '#' && strcmp(key, "#format") == 0)
   1.241 -	{
   1.242 -		if (pPhrase->fmt_list != -1)
   1.243 -		{
   1.244 -			ParseWarning("Ignoring duplicated #format property on line %d", states->line);
   1.245 -			return SMCResult_Continue;
   1.246 -		}
   1.247 -
   1.248 -		if (pPhrase->translations > 0)
   1.249 -		{
   1.250 -			ParseWarning("#format property should come before translations on line %d, ignoring", states->line);
   1.251 -			return SMCResult_Continue;
   1.252 -		}
   1.253 -
   1.254 -		/* Do an initial browsing for error checking and what not */
   1.255 -		enum ParseStates
   1.256 -		{
   1.257 -			Parse_None,
   1.258 -			Parse_Index,
   1.259 -			Parse_Format,
   1.260 -		};
   1.261 -
   1.262 -		ParseStates state = Parse_None;
   1.263 -
   1.264 -		const char *old_value = value;
   1.265 -		const char *last_value = value;
   1.266 -		while (*value != '\0')
   1.267 -		{
   1.268 -			if (state == Parse_None)
   1.269 -			{
   1.270 -				if (*value == '{')
   1.271 -				{
   1.272 -					pPhrase->fmt_count++;
   1.273 -					state = Parse_Index;
   1.274 -				} 
   1.275 -				else if (*value == ',') 
   1.276 -				{
   1.277 -					/* Do nothing */
   1.278 -				} 
   1.279 -				else 
   1.280 -				{
   1.281 -					unsigned int bytes = textparsers->GetUTF8CharBytes(value);
   1.282 -					if (bytes != 1 || !isalpha(*value))
   1.283 -					{
   1.284 -						ParseWarning("Invalid token '%c' in #format property on line %d.", *value, states->line);
   1.285 -					}
   1.286 -				}
   1.287 -			} 
   1.288 -			else if (state == Parse_Index) 
   1.289 -			{
   1.290 -				if (*value == ':')
   1.291 -				{
   1.292 -					state = Parse_Format;
   1.293 -					if (value - last_value >= 15)
   1.294 -					{
   1.295 -						ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", states->line);
   1.296 -						m_CurPhrase = -1;
   1.297 -						return SMCResult_Continue;
   1.298 -					}
   1.299 -				} 
   1.300 -				else 
   1.301 -				{
   1.302 -					unsigned int bytes = textparsers->GetUTF8CharBytes(value);
   1.303 -					if (bytes != 1 || !isdigit(*value))
   1.304 -					{
   1.305 -						ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
   1.306 -									*value,
   1.307 -									states->line);
   1.308 -						m_CurPhrase = -1;
   1.309 -						return SMCResult_Continue;
   1.310 -					}
   1.311 -				}
   1.312 -			} 
   1.313 -			else if (state == Parse_Format) 
   1.314 -			{
   1.315 -				if (*value == '}')
   1.316 -				{
   1.317 -					state = Parse_None;
   1.318 -					last_value = value + 1;
   1.319 -				}
   1.320 -			}
   1.321 -			value++;
   1.322 -		}
   1.323 -
   1.324 -		if (state != Parse_None)
   1.325 -		{
   1.326 -			/* Moose clam cow. */
   1.327 -			ParseWarning("Unterminated format string on line %d, phrase will be ignored.", states->line);
   1.328 -			m_CurPhrase = -1;
   1.329 -			return SMCResult_Continue;
   1.330 -		}
   1.331 -		value = old_value;
   1.332 -
   1.333 -		/* Allocate the format table */
   1.334 -		char fmt_buf[16];
   1.335 -		int *fmt_list;
   1.336 -		int tmp = m_pMemory->CreateMem(sizeof(int) * pPhrase->fmt_count, (void **)&fmt_list);
   1.337 -
   1.338 -		/* Update the phrase pointer in case it changed */
   1.339 -		pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.340 -		pPhrase->fmt_list = tmp;
   1.341 -
   1.342 -		/* Initialize */
   1.343 -		for (size_t i=0; i<pPhrase->fmt_count; i++)
   1.344 -		{
   1.345 -			fmt_list[i] = -1;
   1.346 -		}
   1.347 -
   1.348 -		/* Now we need to read again... this time into the format buffer */
   1.349 -		const char *in_ptr = value;
   1.350 -		const char *idx_ptr = NULL;
   1.351 -		char *out_ptr = NULL;
   1.352 -		unsigned int cur_idx = 0;
   1.353 -		state = Parse_None;
   1.354 -		while (*in_ptr != '\0')
   1.355 -		{
   1.356 -			if (state == Parse_None)
   1.357 -			{
   1.358 -				if (*in_ptr == '{')
   1.359 -				{
   1.360 -					state = Parse_Index;
   1.361 -					idx_ptr = NULL;
   1.362 -				}
   1.363 -			}
   1.364 -			else if (state == Parse_Index) 
   1.365 -			{
   1.366 -				if (*in_ptr == ':')
   1.367 -				{
   1.368 -					/* Check the number! */
   1.369 -					if (!idx_ptr)
   1.370 -					{
   1.371 -						ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", states->line);
   1.372 -						m_CurPhrase = -1;
   1.373 -						return SMCResult_Continue;
   1.374 -					}
   1.375 -					long idx = strtol(idx_ptr, NULL, 10);
   1.376 -					if (idx < 1 || idx > (long)pPhrase->fmt_count)
   1.377 -					{
   1.378 -						ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, states->line);
   1.379 -						m_CurPhrase = -1;
   1.380 -						return SMCResult_Continue;
   1.381 -					}
   1.382 -					else if (fmt_list[idx - 1] != -1) 
   1.383 -					{
   1.384 -						ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, states->line);
   1.385 -						m_CurPhrase = -1;
   1.386 -						return SMCResult_Continue;
   1.387 -					}
   1.388 -					cur_idx = (unsigned int)idx;
   1.389 -					state = Parse_Format;
   1.390 -					out_ptr = NULL;
   1.391 -				}
   1.392 -				else if (!idx_ptr) 
   1.393 -				{
   1.394 -					idx_ptr = in_ptr;
   1.395 -				}
   1.396 -			}
   1.397 -			else if (state == Parse_Format) 
   1.398 -			{
   1.399 -				if (*in_ptr == '}')
   1.400 -				{
   1.401 -					if (!out_ptr)
   1.402 -					{
   1.403 -						ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", states->line);
   1.404 -						m_CurPhrase = -1;
   1.405 -						return SMCResult_Continue;
   1.406 -					}
   1.407 -					*out_ptr = '\0';
   1.408 -					state = Parse_None;
   1.409 -					/* Now, add this to our table */
   1.410 -					int tmp_idx = m_pStringTab->AddString(fmt_buf);
   1.411 -					/* Update pointers and update necessary variables */
   1.412 -					pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.413 -					pPhrase->fmt_bytes += strlen(fmt_buf);
   1.414 -					fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
   1.415 -					fmt_list[cur_idx - 1] = tmp_idx;
   1.416 -				} 
   1.417 -				else 
   1.418 -				{
   1.419 -					if (!out_ptr)
   1.420 -					{
   1.421 -						out_ptr = fmt_buf;
   1.422 -						*out_ptr++ = '%';
   1.423 -					}
   1.424 -					/* Check length ... */
   1.425 -					if ((unsigned)(out_ptr - fmt_buf) >= sizeof(fmt_buf) - 1)
   1.426 -					{
   1.427 -						ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
   1.428 -									 states->line);
   1.429 -						m_CurPhrase = -1;
   1.430 -						return SMCResult_Continue;
   1.431 -					}
   1.432 -					*out_ptr++ = *in_ptr;
   1.433 -				}
   1.434 -			}
   1.435 -			in_ptr++;
   1.436 -		}
   1.437 -
   1.438 -		/* If we've gotten here, we only accepted unique indexes in a range.
   1.439 -		 * Therefore, the list has to be completely filled.  Double check anyway.
   1.440 -		 */
   1.441 -		for (size_t i=0; i<pPhrase->fmt_count; i++)
   1.442 -		{
   1.443 -			if (fmt_list[i] == -1)
   1.444 -			{
   1.445 -				ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
   1.446 -							 i + 1,
   1.447 -							 states->line);
   1.448 -				m_CurPhrase = -1;
   1.449 -				return SMCResult_Continue;
   1.450 -			}
   1.451 -		}
   1.452 -	} 
   1.453 -	else 
   1.454 -	{
   1.455 -		size_t len = strlen(key);
   1.456 -		if (len != 2)
   1.457 -		{
   1.458 -			ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, states->line);
   1.459 -			return SMCResult_Continue;
   1.460 -		}
   1.461 -
   1.462 -		unsigned int lang;
   1.463 -		if (!m_pTranslator->GetLanguageByCode(key, &lang))
   1.464 -		{
   1.465 -			/* Ignore if we don't have a language.
   1.466 -			 * :IDEA: issue a one-time alert?
   1.467 -			 */
   1.468 -			return SMCResult_Continue;
   1.469 -		}
   1.470 -
   1.471 -		/* See how many bytes we need for this string, then allocate.
   1.472 -		 * NOTE: THIS SHOULD GUARANTEE THAT WE DO NOT NEED TO NEED TO SIZE CHECK 
   1.473 -		 */
   1.474 -		len = strlen(value) + pPhrase->fmt_bytes + 1;
   1.475 -		char *out_buf;
   1.476 -		int out_idx;
   1.477 -
   1.478 -		out_idx = m_pMemory->CreateMem(len, (void **)&out_buf);
   1.479 -
   1.480 -		/* Update pointer */
   1.481 -		pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.482 -
   1.483 -		int *fmt_order;
   1.484 -		int *fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
   1.485 -		trans_t *pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
   1.486 -
   1.487 -		pTrans = &pTrans[lang];
   1.488 -		pTrans->stridx = out_idx;
   1.489 -
   1.490 -		bool params[MAX_TRANSLATE_PARAMS];
   1.491 -
   1.492 -		/* Build the format order list, if necessary */
   1.493 -		if (fmt_list)
   1.494 -		{
   1.495 -			int tmp = m_pMemory->CreateMem(pPhrase->fmt_count * sizeof(int), (void **)&fmt_order);
   1.496 -			
   1.497 -			/* Update pointers */
   1.498 -			pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
   1.499 -			pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
   1.500 -			fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
   1.501 -			out_buf = (char *)m_pMemory->GetAddress(out_idx);
   1.502 -			pTrans = &pTrans[lang];
   1.503 -
   1.504 -			/* Now it's safe to save the index */
   1.505 -			pTrans->fmt_order = tmp;
   1.506 -
   1.507 -			for (unsigned int i=0; i<pPhrase->fmt_count; i++)
   1.508 -			{
   1.509 -				fmt_order[i] = -1;
   1.510 -			}
   1.511 -			memset(&params[0], 0, sizeof(params));
   1.512 -		}
   1.513 -
   1.514 -		const char *in_ptr = value;
   1.515 -		char *out_ptr = out_buf;
   1.516 -		unsigned int order_idx = 0;
   1.517 -		while (*in_ptr != '\0')
   1.518 -		{
   1.519 -			if (*in_ptr == '\\')
   1.520 -			{
   1.521 -				switch (*(in_ptr + 1))
   1.522 -				{
   1.523 -				case '\n':
   1.524 -					*out_ptr++ = '\n';
   1.525 -					break;
   1.526 -				case '\t':
   1.527 -					*out_ptr++ = '\t';
   1.528 -					break;
   1.529 -				case '\r':
   1.530 -					*out_ptr++ = '\r';
   1.531 -					break;
   1.532 -				case '{':
   1.533 -					*out_ptr++ = '{';
   1.534 -					break;
   1.535 -				default:
   1.536 -					/* Copy both bytes since we don't know what's going on */
   1.537 -					*out_ptr++ = *in_ptr++;
   1.538 -					*out_ptr++ = *in_ptr;
   1.539 -					break;
   1.540 -				}
   1.541 -				/* Skip past the last byte read */
   1.542 -				in_ptr++;
   1.543 -			} 
   1.544 -			else if (*in_ptr == '{' && fmt_list != NULL) 
   1.545 -			{
   1.546 -				/* Search for parameters if this is a formatted string */
   1.547 -				const char *scrap_in_point = in_ptr;
   1.548 -				const char *digit_start = ++in_ptr;
   1.549 -				unsigned int bytes;
   1.550 -				while (*in_ptr != '\0')
   1.551 -				{
   1.552 -					bytes = textparsers->GetUTF8CharBytes(in_ptr);
   1.553 -					if (bytes != 1)
   1.554 -					{
   1.555 -						goto scrap_point;
   1.556 -					}
   1.557 -					if (*in_ptr == '}')
   1.558 -					{
   1.559 -						/* Did we get an index? */
   1.560 -						if (in_ptr == digit_start)
   1.561 -						{
   1.562 -							goto scrap_point;
   1.563 -						}
   1.564 -						/* Is it valid? */
   1.565 -						long idx = strtol(digit_start, NULL, 10);
   1.566 -						if (idx < 1 || idx > (int)pPhrase->fmt_count)
   1.567 -						{
   1.568 -							goto scrap_point;
   1.569 -						}
   1.570 -						if (params[idx-1])
   1.571 -						{
   1.572 -							goto scrap_point;
   1.573 -						}
   1.574 -
   1.575 -						/* We're safe to insert the string.  First mark order. */
   1.576 -						fmt_order[order_idx++] = (int)idx - 1;
   1.577 -						/* Now concatenate */
   1.578 -						out_ptr += sprintf(out_ptr, "%s", m_pStringTab->GetString(fmt_list[idx-1]));
   1.579 -						/* Mark as used */
   1.580 -						params[idx-1] = true;
   1.581 -
   1.582 -						goto cont_loop;
   1.583 -					}
   1.584 -					in_ptr++;
   1.585 -				}
   1.586 -scrap_point:
   1.587 -				/* Pretend none of this ever happened.  Move along now! */
   1.588 -				in_ptr = scrap_in_point;
   1.589 -				*out_ptr++ = *in_ptr;
   1.590 -			} else {
   1.591 -				*out_ptr++ = *in_ptr;
   1.592 -			}
   1.593 -cont_loop:
   1.594 -			in_ptr++;
   1.595 -		}
   1.596 -		*out_ptr = '\0';
   1.597 -		pPhrase->translations++;
   1.598 -	}
   1.599 -
   1.600 -	return SMCResult_Continue;
   1.601 -}
   1.602 -
   1.603 -SMCResult CPhraseFile::ReadSMC_LeavingSection(const SMCStates *states)
   1.604 -{
   1.605 -	if (m_ParseState == PPS_InPhrase)
   1.606 -	{
   1.607 -		if (m_CurPhrase == -1 && m_LastPhraseString.size())
   1.608 -		{
   1.609 -			sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
   1.610 -		}
   1.611 -		m_CurPhrase = -1;
   1.612 -		m_ParseState = PPS_Phrases;
   1.613 -		m_LastPhraseString.assign("");
   1.614 -	} 
   1.615 -	else if (m_ParseState == PPS_Phrases) 
   1.616 -	{
   1.617 -		m_ParseState = PPS_None;
   1.618 -	}
   1.619 -
   1.620 -	return SMCResult_Continue;
   1.621 -}
   1.622 -
   1.623 -void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed)
   1.624 -{
   1.625 -	/* Check to see if we have any dangling phrases that weren't completed, and scrap them */
   1.626 -	if ((halted || failed) && m_LastPhraseString.size())
   1.627 -	{
   1.628 -		sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
   1.629 -	}
   1.630 -}
   1.631 -
   1.632 -TransError CPhraseFile::GetTranslation(const char *szPhrase, unsigned int lang_id, Translation *pTrans)
   1.633 -{
   1.634 -	if (lang_id >= m_LangCount)
   1.635 -	{
   1.636 -		return Trans_BadLanguage;
   1.637 -	}
   1.638 -
   1.639 -	void *object;
   1.640 -	if (!sm_trie_retrieve(m_pPhraseLookup, szPhrase, &object))
   1.641 -	{
   1.642 -		return Trans_BadPhrase;
   1.643 -	}
   1.644 -
   1.645 -	phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(reinterpret_cast<int>(object));
   1.646 -	trans_t *trans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
   1.647 -
   1.648 -	trans = &trans[lang_id];
   1.649 -
   1.650 -	if (trans->stridx == -1)
   1.651 -	{
   1.652 -		return Trans_BadPhraseLanguage;
   1.653 -	}
   1.654 -
   1.655 -	pTrans->fmt_count = pPhrase->fmt_count;
   1.656 -	pTrans->fmt_order = pTrans->fmt_count > 0 ? (int *)m_pMemory->GetAddress(trans->fmt_order) : NULL;
   1.657 -	pTrans->szPhrase = m_pStringTab->GetString(trans->stridx);
   1.658 -
   1.659 -	return Trans_Okay;
   1.660 -}
   1.661 -
   1.662 -const char *CPhraseFile::GetFilename()
   1.663 -{
   1.664 -	return m_File.c_str();
   1.665 -}
   1.666 -
   1.667 -/**************************
   1.668 - ** MAIN TRANSLATOR CODE **
   1.669 - **************************/
   1.670 -
   1.671 -Translator::Translator() : m_ServerLang(SOURCEMOD_LANGUAGE_ENGLISH)
   1.672 -{
   1.673 -	m_pStringTab = new BaseStringTable(2048);
   1.674 -	m_pLCodeLookup = sm_trie_create();
   1.675 -	strncopy(m_InitialLang, "en", sizeof(m_InitialLang));
   1.676 -}
   1.677 -
   1.678 -Translator::~Translator()
   1.679 -{
   1.680 -	for (size_t i=0; i<m_Files.size(); i++)
   1.681 -	{
   1.682 -		delete m_Files[i];
   1.683 -	}
   1.684 -
   1.685 -	for (size_t i=0; i<m_Languages.size(); i++)
   1.686 -	{
   1.687 -		delete m_Languages[i];
   1.688 -	}
   1.689 -
   1.690 -	sm_trie_destroy(m_pLCodeLookup);
   1.691 -
   1.692 -	delete m_pStringTab;
   1.693 -}
   1.694 -
   1.695 -ConfigResult Translator::OnSourceModConfigChanged(const char *key, 
   1.696 -									  const char *value, 
   1.697 -									  ConfigSource source, 
   1.698 -									  char *error, 
   1.699 -									  size_t maxlength)
   1.700 -{
   1.701 -	if (strcasecmp(key, "ServerLang") == 0)
   1.702 -	{
   1.703 -		if (source == ConfigSource_Console)
   1.704 -		{
   1.705 -			unsigned int index;
   1.706 -			if (!GetLanguageByCode(value, &index))
   1.707 -			{
   1.708 -				UTIL_Format(error, maxlength, "Language code \"%s\" is not registered", value);
   1.709 -				return ConfigResult_Reject;
   1.710 -			}
   1.711 -
   1.712 -			m_ServerLang = index;
   1.713 -		} else {
   1.714 -			strncopy(m_InitialLang, value, sizeof(m_InitialLang));
   1.715 -		}
   1.716 -
   1.717 -		return ConfigResult_Accept;
   1.718 -	}
   1.719 -
   1.720 -	return ConfigResult_Ignore;
   1.721 -}
   1.722 -
   1.723 -void Translator::OnSourceModLevelChange(const char *mapName)
   1.724 -{
   1.725 -	/* Refresh language stuff */
   1.726 -	char path[PLATFORM_MAX_PATH];
   1.727 -	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/languages.cfg");
   1.728 -	RebuildLanguageDatabase(path);
   1.729 -}
   1.730 -
   1.731 -void Translator::OnSourceModAllInitialized()
   1.732 -{
   1.733 -	AddLanguage("en", "English");
   1.734 -
   1.735 -	g_pCorePhrases = CreatePhraseCollection();
   1.736 -	g_pCorePhrases->AddPhraseFile("core.phrases");
   1.737 -
   1.738 -	g_ShareSys.AddInterface(NULL, this);
   1.739 -}
   1.740 -
   1.741 -void Translator::OnSourceModShutdown()
   1.742 -{
   1.743 -	g_pCorePhrases->Destroy();
   1.744 -}
   1.745 -
   1.746 -bool Translator::GetLanguageByCode(const char *code, unsigned int *index)
   1.747 -{
   1.748 -	void *_index;
   1.749 -
   1.750 -	if (!sm_trie_retrieve(m_pLCodeLookup, code, &_index))
   1.751 -	{
   1.752 -		return false;
   1.753 -	}
   1.754 -
   1.755 -	if (index)
   1.756 -	{
   1.757 -		*index = reinterpret_cast<unsigned int>(_index);
   1.758 -	}
   1.759 -
   1.760 -	return true;
   1.761 -}
   1.762 -
   1.763 -bool Translator::GetLanguageByName(const char *name, unsigned int *index)
   1.764 -{
   1.765 -	CVector<Language *>::iterator iter;
   1.766 -	unsigned int id = 0;
   1.767 -
   1.768 -	for (iter=m_Languages.begin(); iter!=m_Languages.end(); iter++, id++)
   1.769 -	{
   1.770 -		if (strcasecmp(m_pStringTab->GetString((*iter)->m_FullName), name) == 0)
   1.771 -		{
   1.772 -			break;
   1.773 -		}
   1.774 -	}
   1.775 -
   1.776 -	if (iter == m_Languages.end())
   1.777 -	{
   1.778 -		return false;
   1.779 -	}
   1.780 -
   1.781 -	if (index)
   1.782 -	{
   1.783 -		*index = id;
   1.784 -	}
   1.785 -
   1.786 -	return true;
   1.787 -}
   1.788 -
   1.789 -unsigned int Translator::GetLanguageCount()
   1.790 -{
   1.791 -	return (unsigned int)m_Languages.size();
   1.792 -}
   1.793 -
   1.794 -BaseStringTable *Translator::GetStringTable()
   1.795 -{
   1.796 -	return m_pStringTab;
   1.797 -}
   1.798 -
   1.799 -unsigned int Translator::FindOrAddPhraseFile(const char *phrase_file)
   1.800 -{
   1.801 -	for (size_t i=0; i<m_Files.size(); i++)
   1.802 -	{
   1.803 -		if (strcmp(m_Files[i]->GetFilename(), phrase_file) == 0)
   1.804 -		{
   1.805 -			return (unsigned int)i;
   1.806 -		}
   1.807 -	}
   1.808 -
   1.809 -	CPhraseFile *pFile = new CPhraseFile(this, phrase_file);
   1.810 -	unsigned int idx = (unsigned int)m_Files.size();
   1.811 -	
   1.812 -	m_Files.push_back(pFile);
   1.813 -
   1.814 -	pFile->ReparseFile();
   1.815 -
   1.816 -	return idx;
   1.817 -}
   1.818 -
   1.819 -void Translator::RebuildLanguageDatabase(const char *lang_header_file)
   1.820 -{
   1.821 -	/* Erase everything we have */
   1.822 -	sm_trie_destroy(m_pLCodeLookup);
   1.823 -	m_pLCodeLookup = sm_trie_create();
   1.824 -	m_pStringTab->Reset();
   1.825 -
   1.826 -	for (size_t i=0; i<m_Languages.size(); i++)
   1.827 -	{
   1.828 -		delete m_Languages[i];
   1.829 -	}
   1.830 -	m_Languages.clear();
   1.831 -
   1.832 -	/* Start anew */
   1.833 -	SMCError err;
   1.834 -	SMCStates states;
   1.835 -	if ((err=textparsers->ParseFile_SMC(lang_header_file, this, &states)) != SMCError_Okay)
   1.836 -	{
   1.837 -		const char *str_err = textparsers->GetSMCErrorString(err);
   1.838 -		if (!str_err)
   1.839 -		{
   1.840 -			str_err = m_CustomError.c_str();
   1.841 -		}
   1.842 -
   1.843 -		g_Logger.LogError("[SM] Failed to parse language header file: \"%s\"", lang_header_file);
   1.844 -		g_Logger.LogError("[SM] Parse error (line %d, column %d): %s", states.line, states.col, str_err);
   1.845 -	}
   1.846 -
   1.847 -	void *serverLang;
   1.848 -
   1.849 -	if (!sm_trie_retrieve(m_pLCodeLookup, m_InitialLang, &serverLang))
   1.850 -	{
   1.851 -		g_Logger.LogError("Server language was set to bad language \"%s\" -- reverting to English", m_InitialLang);
   1.852 -
   1.853 -		strncopy(m_InitialLang, "en", sizeof(m_InitialLang));
   1.854 -		m_ServerLang = SOURCEMOD_LANGUAGE_ENGLISH;
   1.855 -	}
   1.856 -
   1.857 -	m_ServerLang = reinterpret_cast<unsigned int>(serverLang);
   1.858 -
   1.859 -	if (!m_Languages.size())
   1.860 -	{
   1.861 -		g_Logger.LogError("[SM] Fatal error, no languages found! Translation will not work.");
   1.862 -	}
   1.863 -
   1.864 -	for (size_t i=0; i<m_Files.size(); i++)
   1.865 -	{
   1.866 -		m_Files[i]->ReparseFile();
   1.867 -	}
   1.868 -}
   1.869 -
   1.870 -void Translator::ReadSMC_ParseStart()
   1.871 -{
   1.872 -	m_InLanguageSection = false;
   1.873 -	m_CustomError.clear();
   1.874 -}
   1.875 -
   1.876 -SMCResult Translator::ReadSMC_NewSection(const SMCStates *states, const char *name)
   1.877 -{
   1.878 -	if (!m_InLanguageSection)
   1.879 -	{
   1.880 -		if (strcmp(name, "Languages") == 0)
   1.881 -		{
   1.882 -			m_InLanguageSection = true;
   1.883 -		}
   1.884 -	}
   1.885 -
   1.886 -	if (!m_InLanguageSection)
   1.887 -	{
   1.888 -		g_Logger.LogError("[SM] Warning: Unrecognized section \"%s\" in languages.cfg", name);
   1.889 -	}
   1.890 -
   1.891 -	return SMCResult_Continue;
   1.892 -}
   1.893 -
   1.894 -SMCResult Translator::ReadSMC_LeavingSection(const SMCStates *states)
   1.895 -{
   1.896 -	m_InLanguageSection = false;
   1.897 -
   1.898 -	return SMCResult_Continue;
   1.899 -}
   1.900 -
   1.901 -SMCResult Translator::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   1.902 -{
   1.903 -	size_t len = strlen(key);
   1.904 -
   1.905 -	if (len != 2)
   1.906 -	{
   1.907 -		g_Logger.LogError("[SM] Warning encountered parsing languages.cfg file.");
   1.908 -		g_Logger.LogError("[SM] Invalid language code \"%s\" is being ignored.", key);
   1.909 -	}
   1.910 -
   1.911 -	AddLanguage(key, value);
   1.912 -
   1.913 -	return SMCResult_Continue;
   1.914 -}
   1.915 -
   1.916 -bool Translator::AddLanguage(const char *langcode, const char *description)
   1.917 -{
   1.918 -	if (sm_trie_retrieve(m_pLCodeLookup, langcode, NULL))
   1.919 -	{
   1.920 -		return false;
   1.921 -	}
   1.922 -
   1.923 -	Language *pLanguage = new Language;
   1.924 -	unsigned int idx = m_Languages.size();
   1.925 -
   1.926 -	pLanguage->m_code2[0] = langcode[0];
   1.927 -	pLanguage->m_code2[1] = langcode[1];
   1.928 -	pLanguage->m_code2[2] = langcode[2];
   1.929 -	pLanguage->m_FullName = m_pStringTab->AddString(description);
   1.930 -
   1.931 -	sm_trie_insert(m_pLCodeLookup, langcode, reinterpret_cast<void *>(idx));
   1.932 -
   1.933 -	m_Languages.push_back(pLanguage);
   1.934 -
   1.935 -	return true;
   1.936 -}
   1.937 -
   1.938 -CPhraseFile *Translator::GetFileByIndex(unsigned int index)
   1.939 -{
   1.940 -	if (index >= m_Files.size())
   1.941 -	{
   1.942 -		return NULL;
   1.943 -	}
   1.944 -
   1.945 -	return m_Files[index];
   1.946 -}
   1.947 -
   1.948 -unsigned int Translator::GetServerLanguage()
   1.949 -{
   1.950 -	return m_ServerLang;
   1.951 -}
   1.952 -
   1.953 -unsigned int Translator::GetClientLanguage(int client)
   1.954 -{
   1.955 -	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   1.956 -	return pPlayer->GetLanguageId();
   1.957 -}
   1.958 -
   1.959 -bool Translator::GetLanguageInfo(unsigned int number, const char **code, const char **name)
   1.960 -{
   1.961 -	if (number >= GetLanguageCount())
   1.962 -	{
   1.963 -		return false;
   1.964 -	}
   1.965 -
   1.966 -	Language *l = m_Languages[number];
   1.967 -	if (code)
   1.968 -	{
   1.969 -		*code = l->m_code2;
   1.970 -	}
   1.971 -	if (name)
   1.972 -	{
   1.973 -		*name = m_pStringTab->GetString(l->m_FullName);
   1.974 -	}
   1.975 -
   1.976 -	return true;
   1.977 -}
   1.978 -
   1.979 -const char *Translator::GetInterfaceName()
   1.980 -{
   1.981 -	return SMINTERFACE_TRANSLATOR_NAME;
   1.982 -}
   1.983 -
   1.984 -unsigned int Translator::GetInterfaceVersion()
   1.985 -{
   1.986 -	return SMINTERFACE_TRANSLATOR_VERSION;
   1.987 -}
   1.988 -
   1.989 -IPhraseCollection *Translator::CreatePhraseCollection()
   1.990 -{
   1.991 -	return new CPhraseCollection();
   1.992 -}
   1.993 -
   1.994 -int Translator::SetGlobalTarget(int index)
   1.995 -{
   1.996 -	return g_SourceMod.SetGlobalTarget(index);
   1.997 -}
   1.998 -
   1.999 -int Translator::GetGlobalTarget() const
  1.1000 -{
  1.1001 -	return g_SourceMod.GetGlobalTarget();
  1.1002 -}
  1.1003 -
  1.1004 -bool CoreTranslate(char *buffer, 
  1.1005 -				   size_t maxlength,
  1.1006 -				   const char *format,
  1.1007 -				   unsigned int numparams,
  1.1008 -				   size_t *pOutLength,
  1.1009 -				   ...)
  1.1010 -{
  1.1011 -	va_list ap;
  1.1012 -	unsigned int i;
  1.1013 -	const char *fail_phrase;
  1.1014 -	void *params[MAX_TRANSLATE_PARAMS];
  1.1015 -
  1.1016 -	if (numparams > MAX_TRANSLATE_PARAMS)
  1.1017 -	{
  1.1018 -		assert(false);
  1.1019 -		return false;
  1.1020 -	}
  1.1021 -
  1.1022 -	va_start(ap, pOutLength);
  1.1023 -	for (i = 0; i < numparams; i++)
  1.1024 -	{
  1.1025 -		params[i] = va_arg(ap, void *);
  1.1026 -	}
  1.1027 -	va_end(ap);
  1.1028 -
  1.1029 -	if (!g_pCorePhrases->FormatString(buffer,
  1.1030 -		maxlength, 
  1.1031 -		format, 
  1.1032 -		params,
  1.1033 -		numparams,
  1.1034 -		pOutLength,
  1.1035 -		&fail_phrase))
  1.1036 -	{
  1.1037 -		if (fail_phrase != NULL)
  1.1038 -		{
  1.1039 -			g_Logger.LogError("[SM] Could not find core phrase: %s", fail_phrase);
  1.1040 -		}
  1.1041 -		else
  1.1042 -		{
  1.1043 -			g_Logger.LogError("[SM] Unknown fatal error while translating a core phrase.");
  1.1044 -		}
  1.1045 -
  1.1046 -		return false;
  1.1047 -	}
  1.1048 -
  1.1049 -	return true;
  1.1050 -}
  1.1051 -
  1.1052 -bool Translator::FormatString(char *buffer,
  1.1053 -							  size_t maxlength,
  1.1054 -							  const char *format,
  1.1055 -							  IPhraseCollection *pPhrases,
  1.1056 -							  void **params,
  1.1057 -							  unsigned int numparams,
  1.1058 -							  size_t *pOutLength,
  1.1059 -							  const char **pFailPhrase)
  1.1060 -{
  1.1061 -	unsigned int arg;
  1.1062 -
  1.1063 -	arg = 0;
  1.1064 -	if (!gnprintf(buffer, maxlength, format, pPhrases, params, numparams, arg, pOutLength, pFailPhrase))
  1.1065 -	{
  1.1066 -		return false;
  1.1067 -	}
  1.1068 -
  1.1069 -	if (arg != numparams)
  1.1070 -	{
  1.1071 -		if (pFailPhrase != NULL)
  1.1072 -		{
  1.1073 -			*pFailPhrase = NULL;
  1.1074 -		}
  1.1075 -		return false;
  1.1076 -	}
  1.1077 -
  1.1078 -	return true;
  1.1079 -}