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