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