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