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