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