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