initial import of translator code (tested)
--HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40281
This commit is contained in:
		
							parent
							
								
									b3ddba4832
								
							
						
					
					
						commit
						95368e57cd
					
				
							
								
								
									
										748
									
								
								core/CTranslator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										748
									
								
								core/CTranslator.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,748 @@
 | 
				
			|||||||
 | 
					#include <stdarg.h>
 | 
				
			||||||
 | 
					#include "CTranslator.h"
 | 
				
			||||||
 | 
					#include "CLogger.h"
 | 
				
			||||||
 | 
					#include "CTextParsers.h"
 | 
				
			||||||
 | 
					#include "LibrarySys.h"
 | 
				
			||||||
 | 
					#include "sm_stringutil.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CTranslator g_Translator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct trans_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int stridx;
 | 
				
			||||||
 | 
						int fmt_order;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct phrase_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int fmt_list;
 | 
				
			||||||
 | 
						unsigned int fmt_count;
 | 
				
			||||||
 | 
						unsigned int fmt_bytes;
 | 
				
			||||||
 | 
						int trans_tbl;
 | 
				
			||||||
 | 
						unsigned int translations;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CPhraseFile::CPhraseFile(CTranslator *pTranslator, const char *file)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_pStringTab = pTranslator->GetStringTable();
 | 
				
			||||||
 | 
						m_pMemory = m_pStringTab->GetMemTable();
 | 
				
			||||||
 | 
						m_LangCount = pTranslator->GetLanguageCount();
 | 
				
			||||||
 | 
						m_File.assign(file);
 | 
				
			||||||
 | 
						m_pTranslator = pTranslator;
 | 
				
			||||||
 | 
						m_pPhraseLookup = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CPhraseFile::~CPhraseFile()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_pPhraseLookup)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							sm_trie_destroy(m_pPhraseLookup);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CPhraseFile::ParseError(const char *message, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char buffer[1024];
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						va_start(ap, message);
 | 
				
			||||||
 | 
						vsnprintf(buffer, sizeof(buffer), message, ap);
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_ParseError.assign(buffer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CPhraseFile::ParseWarning(const char *message, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char buffer[1024];
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						va_start(ap, message);
 | 
				
			||||||
 | 
						vsnprintf(buffer, sizeof(buffer), message, ap);
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!m_FileLogged)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Warning(s) encountered in translation file \"%s\"", m_File.c_str());
 | 
				
			||||||
 | 
							m_FileLogged;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_Logger.LogError("[SOURCEMOD] %s", message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CPhraseFile::ReparseFile()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_pPhraseLookup)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							sm_trie_destroy(m_pPhraseLookup);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m_pPhraseLookup = sm_trie_create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_LangCount = m_pTranslator->GetLanguageCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!m_LangCount)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SMCParseError err;
 | 
				
			||||||
 | 
						char path[PLATFORM_MAX_PATH+1];
 | 
				
			||||||
 | 
						g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/translations/%s", g_SourceMod.GetSMBaseDir(), m_File.c_str());
 | 
				
			||||||
 | 
						unsigned int line=0, col=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((err=g_TextParser.ParseFile_SMC(path, this, &line, &col)) != SMCParse_Okay)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const char *msg = g_TextParser.GetSMCErrorString(err);
 | 
				
			||||||
 | 
							if (!msg)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								msg = m_ParseError.c_str();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Fatal error encountered parsing translation file \"%s\"", m_File.c_str());
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Error (line %d, column %d): %s", line, col, msg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CPhraseFile::ReadSMC_ParseStart()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_CurPhrase = -1;
 | 
				
			||||||
 | 
						m_ParseState = PPS_None;
 | 
				
			||||||
 | 
						m_CurLine = 0;
 | 
				
			||||||
 | 
						m_FileLogged = false;
 | 
				
			||||||
 | 
						m_LastPhraseString.clear();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_CurLine = curline;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool recognized = false;
 | 
				
			||||||
 | 
						if (m_ParseState == PPS_None)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (strcmp(name, "Phrases") == 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_ParseState = PPS_Phrases;
 | 
				
			||||||
 | 
								recognized = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (m_ParseState == PPS_Phrases) {
 | 
				
			||||||
 | 
							m_ParseState = PPS_InPhrase;
 | 
				
			||||||
 | 
							recognized = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							phrase_t *pPhrase;
 | 
				
			||||||
 | 
							m_CurPhrase = m_pMemory->CreateMem(sizeof(phrase_t), (void **)&pPhrase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Create the reverse lookup */
 | 
				
			||||||
 | 
							if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast<void *>(m_CurPhrase)))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, m_CurLine);
 | 
				
			||||||
 | 
								/* :IDEA: prevent memory waste by seeking backwards in memtable? */
 | 
				
			||||||
 | 
								m_CurPhrase = -1;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* Initialize new phrase */
 | 
				
			||||||
 | 
								trans_t *pTrans;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
								pPhrase->fmt_count = 0;
 | 
				
			||||||
 | 
								pPhrase->fmt_list = -1;
 | 
				
			||||||
 | 
								pPhrase->trans_tbl = m_pMemory->CreateMem(sizeof(trans_t) * m_LangCount, (void **)&pTrans);
 | 
				
			||||||
 | 
								pPhrase->translations = 0;
 | 
				
			||||||
 | 
								pPhrase->fmt_bytes = 0;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
								for (unsigned int i=0; i<m_LangCount; i++)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									pTrans[i].stridx = -1;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								m_LastPhraseString.assign(name);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (m_ParseState == PPS_InPhrase) {
 | 
				
			||||||
 | 
							ParseError("Phrase sections may not have sub-sections");
 | 
				
			||||||
 | 
							return SMCParse_HaltFail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!recognized)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ParseWarning("Ignoring invalid section \"%s\" on line %d.", name, m_CurLine);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CPhraseFile::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* See if we are ignoring a phrase */
 | 
				
			||||||
 | 
						if (!m_CurPhrase)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return SMCParse_Continue;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(m_CurPhrase);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (key[0] == '#' && strcmp(key, "#format") == 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (pPhrase->fmt_list != -1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine);
 | 
				
			||||||
 | 
								return SMCParse_Continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (pPhrase->translations > 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine);
 | 
				
			||||||
 | 
								return SMCParse_Continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Do an initial browsing for error checking and what not */
 | 
				
			||||||
 | 
							enum ParseStates
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Parse_None,
 | 
				
			||||||
 | 
								Parse_Index,
 | 
				
			||||||
 | 
								Parse_Format,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ParseStates state = Parse_None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const char *old_value = value;
 | 
				
			||||||
 | 
							while (*value != '\0')
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (state == Parse_None)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (*value == '{')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										pPhrase->fmt_count++;
 | 
				
			||||||
 | 
										state = Parse_Index;
 | 
				
			||||||
 | 
									} else if (*value == ',') {
 | 
				
			||||||
 | 
										/* Do nothing */
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
 | 
				
			||||||
 | 
										if (bytes != 1 || !isalpha(*value))
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Invalid token '%c' in #format property on line %d.", *value, m_CurLine);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (state == Parse_Index) {
 | 
				
			||||||
 | 
									if (*value == ':')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										state = Parse_Format;
 | 
				
			||||||
 | 
										if (value - old_value >= 15)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										unsigned int bytes = g_TextParser.GetUTF8CharBytes(value);
 | 
				
			||||||
 | 
										if (bytes != 1 || !isdigit(*value))
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.",
 | 
				
			||||||
 | 
														*value,
 | 
				
			||||||
 | 
														m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (state == Parse_Format) {
 | 
				
			||||||
 | 
									if (*value == '}')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										state = Parse_None;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								value++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (state != Parse_None)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/* Moose clam cow. */
 | 
				
			||||||
 | 
								ParseWarning("Unterminated format string on line %d, phrase will be ignored.", m_CurLine);
 | 
				
			||||||
 | 
								m_CurPhrase = -1;
 | 
				
			||||||
 | 
								return SMCParse_Continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							value = old_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Allocate the format table */
 | 
				
			||||||
 | 
							char fmt_buf[16];
 | 
				
			||||||
 | 
							int *fmt_list;
 | 
				
			||||||
 | 
							pPhrase->fmt_list = m_pMemory->CreateMem(sizeof(int) * pPhrase->fmt_count, (void **)&fmt_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Initialize */
 | 
				
			||||||
 | 
							for (size_t i=0; i<pPhrase->fmt_count; i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								fmt_list[i] = -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Now we need to read again... this time into the format buffer */
 | 
				
			||||||
 | 
							const char *in_ptr = value;
 | 
				
			||||||
 | 
							const char *idx_ptr = NULL;
 | 
				
			||||||
 | 
							char *out_ptr = NULL;
 | 
				
			||||||
 | 
							unsigned int cur_idx = 0;
 | 
				
			||||||
 | 
							state = Parse_None;
 | 
				
			||||||
 | 
							while (*in_ptr != '\0')
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (state == Parse_None)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (*in_ptr == '{')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										state = Parse_Index;
 | 
				
			||||||
 | 
										idx_ptr = NULL;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (state == Parse_Index) {
 | 
				
			||||||
 | 
									if (*in_ptr == ':')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										/* Check the number! */
 | 
				
			||||||
 | 
										if (!idx_ptr)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										long idx = strtol(idx_ptr, NULL, 10);
 | 
				
			||||||
 | 
										if (idx < 1 || idx > (long)pPhrase->fmt_count)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										} else if (fmt_list[idx - 1] != -1) {
 | 
				
			||||||
 | 
											ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										cur_idx = (unsigned int)idx;
 | 
				
			||||||
 | 
										state = Parse_Format;
 | 
				
			||||||
 | 
										out_ptr = NULL;
 | 
				
			||||||
 | 
									} else if (!idx_ptr) {
 | 
				
			||||||
 | 
										idx_ptr = in_ptr;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (state == Parse_Format) {
 | 
				
			||||||
 | 
									if (*in_ptr == '}')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										if (!out_ptr)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										*out_ptr = '\0';
 | 
				
			||||||
 | 
										state = Parse_None;
 | 
				
			||||||
 | 
										/* Now, add this to our table */
 | 
				
			||||||
 | 
										fmt_list[cur_idx - 1] = m_pStringTab->AddString(fmt_buf);
 | 
				
			||||||
 | 
										pPhrase->fmt_bytes += strlen(fmt_buf);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										if (!out_ptr)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											out_ptr = fmt_buf;
 | 
				
			||||||
 | 
											*out_ptr++ = '%';
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										/* Check length ... */
 | 
				
			||||||
 | 
										if (out_ptr - fmt_buf >= sizeof(fmt_buf) - 1)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.",
 | 
				
			||||||
 | 
														 m_CurLine);
 | 
				
			||||||
 | 
											m_CurPhrase = -1;
 | 
				
			||||||
 | 
											return SMCParse_Continue;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										*out_ptr++ = *in_ptr;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								in_ptr++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* If we've gotten here, we only accepted unique indexes in a range.
 | 
				
			||||||
 | 
							 * Therefore, the list has to be completely filled.  Double check anyway.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							for (size_t i=0; i<pPhrase->fmt_count; i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (fmt_list[i] == -1)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.",
 | 
				
			||||||
 | 
												 i + 1,
 | 
				
			||||||
 | 
												 m_CurLine);
 | 
				
			||||||
 | 
									m_CurPhrase = -1;
 | 
				
			||||||
 | 
									return SMCParse_Continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							size_t len = strlen(key);
 | 
				
			||||||
 | 
							if (len != 2)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine);
 | 
				
			||||||
 | 
								return SMCParse_Continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							unsigned int lang;
 | 
				
			||||||
 | 
							if (!m_pTranslator->GetLanguageByCode(key, &lang))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/* Ignore if we don't have a language.
 | 
				
			||||||
 | 
								 * :IDEA: issue a one-time alert?
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								return SMCParse_Continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* See how many bytes we need for this string, then allocate.
 | 
				
			||||||
 | 
							 * NOTE: THIS SHOULD GUARANTEE THAT WE DO NOT NEED TO NEED TO SIZE CHECK 
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							len = strlen(value) + pPhrase->fmt_bytes + 1;
 | 
				
			||||||
 | 
							char *out_buf;
 | 
				
			||||||
 | 
							int out_idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out_idx = m_pMemory->CreateMem(len, (void **)&out_buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int *fmt_order;
 | 
				
			||||||
 | 
							int *fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list);
 | 
				
			||||||
 | 
							trans_t *pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pTrans = &pTrans[lang];
 | 
				
			||||||
 | 
							pTrans->stridx = out_idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool params[MAX_TRANSLATE_PARAMS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Build the format order list, if necessary */
 | 
				
			||||||
 | 
							if (fmt_list)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								pTrans->fmt_order = m_pMemory->CreateMem(pPhrase->fmt_count * sizeof(int), (void **)&fmt_order);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (unsigned int i=0; i<pPhrase->fmt_count; i++)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									fmt_order[i] = -1;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								memset(¶ms[0], 0, sizeof(params));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const char *in_ptr = value;
 | 
				
			||||||
 | 
							char *out_ptr = out_buf;
 | 
				
			||||||
 | 
							unsigned int order_idx = 0;
 | 
				
			||||||
 | 
							while (*in_ptr != '\0')
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (*in_ptr == '\\')
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									switch (*(in_ptr + 1))
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
									case '\n':
 | 
				
			||||||
 | 
										*out_ptr++ = '\n';
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case '\t':
 | 
				
			||||||
 | 
										*out_ptr++ = '\t';
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case '\r':
 | 
				
			||||||
 | 
										*out_ptr++ = '\r';
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case '{':
 | 
				
			||||||
 | 
										*out_ptr++ = '{';
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									default:
 | 
				
			||||||
 | 
										/* Copy both bytes since we don't know what's going on */
 | 
				
			||||||
 | 
										*out_ptr++ = *in_ptr++;
 | 
				
			||||||
 | 
										*out_ptr++ = *in_ptr;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									/* Skip past the last byte read */
 | 
				
			||||||
 | 
									in_ptr++;
 | 
				
			||||||
 | 
								} else if (*in_ptr == '{' && fmt_list != NULL) {
 | 
				
			||||||
 | 
									/* Search for parameters if this is a formatted string */
 | 
				
			||||||
 | 
									const char *scrap_in_point = in_ptr;
 | 
				
			||||||
 | 
									const char *digit_start = ++in_ptr;
 | 
				
			||||||
 | 
									unsigned int bytes;
 | 
				
			||||||
 | 
									while (*in_ptr != '\0')
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										bytes = g_TextParser.GetUTF8CharBytes(in_ptr);
 | 
				
			||||||
 | 
										if (bytes != 1)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											goto scrap_point;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (*in_ptr == '}')
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											/* Did we get an index? */
 | 
				
			||||||
 | 
											if (in_ptr == digit_start)
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												goto scrap_point;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											/* Is it valid? */
 | 
				
			||||||
 | 
											long idx = strtol(digit_start, NULL, 10);
 | 
				
			||||||
 | 
											if (idx < 1 || idx > (int)pPhrase->fmt_count)
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												goto scrap_point;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											if (params[idx-1])
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												goto scrap_point;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											/* We're safe to insert the string.  First mark order. */
 | 
				
			||||||
 | 
											fmt_order[order_idx++] = (int)idx - 1;
 | 
				
			||||||
 | 
											/* Now concatenate */
 | 
				
			||||||
 | 
											out_ptr += sprintf(out_ptr, "%s", m_pStringTab->GetString(fmt_list[idx-1]));
 | 
				
			||||||
 | 
											/* Mark as used */
 | 
				
			||||||
 | 
											params[idx-1] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											goto cont_loop;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										in_ptr++;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					scrap_point:
 | 
				
			||||||
 | 
									/* Pretend none of this ever happened.  Move along now! */
 | 
				
			||||||
 | 
									in_ptr = scrap_in_point;
 | 
				
			||||||
 | 
									*out_ptr++ = *in_ptr;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									*out_ptr++ = *in_ptr;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					cont_loop:
 | 
				
			||||||
 | 
								in_ptr++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*out_ptr = '\0';
 | 
				
			||||||
 | 
							pPhrase->translations++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CPhraseFile::ReadSMC_LeavingSection()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (m_ParseState == PPS_InPhrase)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (m_CurPhrase == -1 && m_LastPhraseString.size())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							m_CurPhrase = -1;
 | 
				
			||||||
 | 
							m_ParseState = PPS_Phrases;
 | 
				
			||||||
 | 
							m_LastPhraseString.assign("");
 | 
				
			||||||
 | 
						} else if (m_ParseState == PPS_Phrases) {
 | 
				
			||||||
 | 
							m_ParseState = PPS_None;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Check to see if we have any dangling phrases that weren't completed, and scrap them */
 | 
				
			||||||
 | 
						if ((halted || failed) && m_LastPhraseString.size())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *CPhraseFile::GetTranslation(const Translation *pTrans, int **fmt_order, unsigned int *fmt_count)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pTrans->lang_id >= m_LangCount)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void *object;
 | 
				
			||||||
 | 
						if (!sm_trie_retrieve(m_pPhraseLookup, pTrans->szPhrase, &object))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(reinterpret_cast<int>(object));
 | 
				
			||||||
 | 
						trans_t *trans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trans = &trans[pTrans->lang_id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (trans->stridx == -1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order);
 | 
				
			||||||
 | 
						*fmt_count = pPhrase->fmt_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m_pStringTab->GetString(trans->stridx);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *CPhraseFile::GetFilename()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return m_File.c_str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CTranslator::CTranslator()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_pStringTab = new BaseStringTable(2048);
 | 
				
			||||||
 | 
						m_pLCodeLookup = sm_trie_create();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CTranslator::~CTranslator()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (size_t i=0; i<m_Files.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							delete m_Files[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i=0; i<m_Languages.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							delete m_Languages[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sm_trie_destroy(m_pLCodeLookup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete m_pStringTab;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool CTranslator::GetLanguageByCode(const char *code, unsigned int *index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sm_trie_retrieve(m_pLCodeLookup, code, &_index))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (index)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							*index = reinterpret_cast<unsigned int>(_index);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned int CTranslator::GetLanguageCount()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (unsigned int)m_Languages.size();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BaseStringTable *CTranslator::GetStringTable()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return m_pStringTab;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned int CTranslator::FindOrAddPhraseFile(const char *phrase_file)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (size_t i=0; i<m_Files.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (strcmp(m_Files[i]->GetFilename(), phrase_file) == 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return (unsigned int)i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CPhraseFile *pFile = new CPhraseFile(this, phrase_file);
 | 
				
			||||||
 | 
						unsigned int idx = (unsigned int)m_Files.size();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						m_Files.push_back(pFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pFile->ReparseFile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return idx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CTranslator::RebuildLanguageDatabase(const char *lang_header_file)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Erase everything we have */
 | 
				
			||||||
 | 
						sm_trie_destroy(m_pLCodeLookup);
 | 
				
			||||||
 | 
						m_pLCodeLookup = sm_trie_create();
 | 
				
			||||||
 | 
						m_pStringTab->Reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i=0; i<m_Languages.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							delete m_Languages[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m_Languages.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Start anew */
 | 
				
			||||||
 | 
						SMCParseError err;
 | 
				
			||||||
 | 
						unsigned int line=0, col=0;
 | 
				
			||||||
 | 
						if ((err=g_TextParser.ParseFile_SMC(lang_header_file, this, &line, &col)) != SMCParse_Okay)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const char *str_err = g_TextParser.GetSMCErrorString(err);
 | 
				
			||||||
 | 
							if (!str_err)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								str_err = m_CustomError.c_str();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Failed to parse language header file: \"%s\"", lang_header_file);
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Parse error (line %d, column %d): %s", line, col, str_err);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!m_Languages.size())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Fatal error, no languages found! Translation will not work.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i=0; i<m_Files.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_Files[i]->ReparseFile();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CTranslator::ReadSMC_ParseStart()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_InLanguageSection = false;
 | 
				
			||||||
 | 
						m_CustomError.clear();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CTranslator::ReadSMC_NewSection(const char *name, bool opt_quotes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!m_InLanguageSection)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (strcmp(name, "Languages") == 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								m_InLanguageSection = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!m_InLanguageSection)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Warning: Unrecognized section \"%s\" in languages.cfg");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CTranslator::ReadSMC_LeavingSection()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_InLanguageSection = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SMCParseResult CTranslator::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t len = strlen(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len != 2)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Warning encountered parsing languages.cfg file.");
 | 
				
			||||||
 | 
							g_Logger.LogError("[SOURCEMOD] Invalid language code \"%s\" is being ignored.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Language *pLanguage = new Language;
 | 
				
			||||||
 | 
						unsigned int idx = m_Languages.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						strcpy(pLanguage->m_code2, key);
 | 
				
			||||||
 | 
						pLanguage->m_FullName = m_pStringTab->AddString(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sm_trie_insert(m_pLCodeLookup, key, reinterpret_cast<void *>(idx));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_Languages.push_back(pLanguage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return SMCParse_Continue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t CTranslator::Translate(char *buffer, size_t maxlength, const Translation *pTrans)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pTrans->file_id >= m_Files.size())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CPhraseFile *pFile = m_Files[pTrans->file_id];
 | 
				
			||||||
 | 
						unsigned int count;
 | 
				
			||||||
 | 
						int *order_table;
 | 
				
			||||||
 | 
						const char *str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str=pFile->GetTranslation(pTrans, &order_table, &count)) == NULL)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void *params[MAX_TRANSLATE_PARAMS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Rewrite the parameter order */
 | 
				
			||||||
 | 
						for (unsigned int i=0; i<count; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							params[i] = pTrans->params[order_table[i]];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return gnprintf(buffer, maxlength, str, params);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										101
									
								
								core/CTranslator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								core/CTranslator.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					#ifndef _INCLUDE_SOURCEMOD_TRANSLATOR_H_
 | 
				
			||||||
 | 
					#define _INCLUDE_SOURCEMOD_TRANSLATOR_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sm_trie.h"
 | 
				
			||||||
 | 
					#include <sh_string.h>
 | 
				
			||||||
 | 
					#include <sh_vector.h>
 | 
				
			||||||
 | 
					#include "sm_globals.h"
 | 
				
			||||||
 | 
					#include "sm_memtable.h"
 | 
				
			||||||
 | 
					#include "ITextParsers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_TRANSLATE_PARAMS		32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* :TODO: write a templatized version of tries? */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace SourceHook;
 | 
				
			||||||
 | 
					class CTranslator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum PhraseParseState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						PPS_None = 0,
 | 
				
			||||||
 | 
						PPS_Phrases,
 | 
				
			||||||
 | 
						PPS_InPhrase,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Language
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char m_code2[3];
 | 
				
			||||||
 | 
						int m_FullName;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Translation
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *szPhrase;
 | 
				
			||||||
 | 
						unsigned int lang_id;
 | 
				
			||||||
 | 
						unsigned int file_id;
 | 
				
			||||||
 | 
						void **params;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CPhraseFile : public ITextListener_SMC
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						CPhraseFile(CTranslator *pTranslator, const char *file);
 | 
				
			||||||
 | 
						~CPhraseFile();
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						void ReparseFile();
 | 
				
			||||||
 | 
						const char *GetFilename();
 | 
				
			||||||
 | 
						const char *GetTranslation(const Translation *pTrans, int **fmt_order, unsigned int *fmt_count);
 | 
				
			||||||
 | 
					public: //ITextListener_SMC
 | 
				
			||||||
 | 
						void ReadSMC_ParseStart();
 | 
				
			||||||
 | 
						void ReadSMC_ParseEnd(bool halted, bool failed);
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_LeavingSection();
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline);
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void ParseError(const char *message, ...);
 | 
				
			||||||
 | 
						void ParseWarning(const char *message, ...);
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Trie *m_pPhraseLookup;
 | 
				
			||||||
 | 
						String m_File;
 | 
				
			||||||
 | 
						CTranslator *m_pTranslator;
 | 
				
			||||||
 | 
						PhraseParseState m_ParseState;
 | 
				
			||||||
 | 
						int m_CurPhrase;
 | 
				
			||||||
 | 
						BaseMemTable *m_pMemory;
 | 
				
			||||||
 | 
						BaseStringTable *m_pStringTab;
 | 
				
			||||||
 | 
						unsigned int m_LangCount;
 | 
				
			||||||
 | 
						String m_ParseError;
 | 
				
			||||||
 | 
						String m_LastPhraseString;
 | 
				
			||||||
 | 
						unsigned int m_CurLine;
 | 
				
			||||||
 | 
						bool m_FileLogged;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CTranslator : public ITextListener_SMC
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						CTranslator();
 | 
				
			||||||
 | 
						~CTranslator();
 | 
				
			||||||
 | 
					public: //ITextListener_SMC
 | 
				
			||||||
 | 
						void ReadSMC_ParseStart();
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
 | 
				
			||||||
 | 
						SMCParseResult ReadSMC_LeavingSection();
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						void RebuildLanguageDatabase(const char *lang_header_file);
 | 
				
			||||||
 | 
						unsigned int FindOrAddPhraseFile(const char *phrase_file);
 | 
				
			||||||
 | 
						BaseStringTable *GetStringTable();
 | 
				
			||||||
 | 
						unsigned int GetLanguageCount();
 | 
				
			||||||
 | 
						bool GetLanguageByCode(const char *code, unsigned int *index);
 | 
				
			||||||
 | 
						size_t Translate(char *buffer, size_t maxlength, const Translation *pTrans);
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						CVector<Language *> m_Languages;
 | 
				
			||||||
 | 
						CVector<CPhraseFile *> m_Files;
 | 
				
			||||||
 | 
						BaseStringTable *m_pStringTab;
 | 
				
			||||||
 | 
						Trie *m_pLCodeLookup;
 | 
				
			||||||
 | 
						bool m_InLanguageSection;
 | 
				
			||||||
 | 
						String m_CustomError;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern CTranslator g_Translator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //_INCLUDE_SOURCEMOD_TRANSLATOR_H_
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="Windows-1252"?>
 | 
					<?xml version="1.0" encoding="Windows-1252"?>
 | 
				
			||||||
<VisualStudioProject
 | 
					<VisualStudioProject
 | 
				
			||||||
	ProjectType="Visual C++"
 | 
						ProjectType="Visual C++"
 | 
				
			||||||
	Version="8,00"
 | 
						Version="8.00"
 | 
				
			||||||
	Name="sourcemod_mm"
 | 
						Name="sourcemod_mm"
 | 
				
			||||||
	ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
 | 
						ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
 | 
				
			||||||
	RootNamespace="sourcemod_mm"
 | 
						RootNamespace="sourcemod_mm"
 | 
				
			||||||
@ -195,6 +195,10 @@
 | 
				
			|||||||
				RelativePath="..\CTextParsers.cpp"
 | 
									RelativePath="..\CTextParsers.cpp"
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
			</File>
 | 
								</File>
 | 
				
			||||||
 | 
								<File
 | 
				
			||||||
 | 
									RelativePath="..\CTranslator.cpp"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
								</File>
 | 
				
			||||||
			<File
 | 
								<File
 | 
				
			||||||
				RelativePath="..\sm_autonatives.cpp"
 | 
									RelativePath="..\sm_autonatives.cpp"
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
@ -265,6 +269,10 @@
 | 
				
			|||||||
				RelativePath="..\CTextParsers.h"
 | 
									RelativePath="..\CTextParsers.h"
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
			</File>
 | 
								</File>
 | 
				
			||||||
 | 
								<File
 | 
				
			||||||
 | 
									RelativePath="..\CTranslator.h"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
								</File>
 | 
				
			||||||
			<File
 | 
								<File
 | 
				
			||||||
				RelativePath="..\sm_autonatives.h"
 | 
									RelativePath="..\sm_autonatives.h"
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
 | 
				
			|||||||
@ -3,18 +3,10 @@
 | 
				
			|||||||
#include <stdarg.h>
 | 
					#include <stdarg.h>
 | 
				
			||||||
#include "sm_stringutil.h"
 | 
					#include "sm_stringutil.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ALT				0x00000001		/* alternate form */
 | 
					 | 
				
			||||||
#define HEXPREFIX		0x00000002		/* add 0x or 0X prefix */
 | 
					 | 
				
			||||||
#define LADJUST			0x00000004		/* left adjustment */
 | 
					#define LADJUST			0x00000004		/* left adjustment */
 | 
				
			||||||
#define LONGDBL			0x00000008		/* long double */
 | 
					 | 
				
			||||||
#define LONGINT			0x00000010		/* long integer */
 | 
					 | 
				
			||||||
#define QUADINT			0x00000020		/* quad integer */
 | 
					 | 
				
			||||||
#define SHORTINT		0x00000040		/* short integer */
 | 
					 | 
				
			||||||
#define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
 | 
					#define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
 | 
				
			||||||
#define FPT				0x00000100		/* floating point number */
 | 
					 | 
				
			||||||
#define to_digit(c)		((c) - '0')
 | 
					#define to_digit(c)		((c) - '0')
 | 
				
			||||||
#define is_digit(c)		((unsigned)to_digit(c) <= 9)
 | 
					#define is_digit(c)		((unsigned)to_digit(c) <= 9)
 | 
				
			||||||
#define to_char(n)		((n) + '0')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//:TODO: fix this macro when we have a debugger
 | 
					//:TODO: fix this macro when we have a debugger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,6 +17,8 @@
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//:TODO: review this code before we choose a license
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
 | 
					void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int size = 0;
 | 
						int size = 0;
 | 
				
			||||||
@ -252,8 +246,180 @@ void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
 | 
				
			|||||||
	*buf_p = buf;
 | 
						*buf_p = buf;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!buffer || !maxlen)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int arg = 0;
 | 
				
			||||||
 | 
						char *buf_p;
 | 
				
			||||||
 | 
						char ch;
 | 
				
			||||||
 | 
						int flags;
 | 
				
			||||||
 | 
						int width;
 | 
				
			||||||
 | 
						int prec;
 | 
				
			||||||
 | 
						int n;
 | 
				
			||||||
 | 
						char sign;
 | 
				
			||||||
 | 
						const char *fmt;
 | 
				
			||||||
 | 
						size_t llen = maxlen - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf_p = buffer;
 | 
				
			||||||
 | 
						fmt = format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (true)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// run through the format string until we hit a '%' or '\0'
 | 
				
			||||||
 | 
							for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								*buf_p++ = ch;
 | 
				
			||||||
 | 
								llen--;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ((ch == '\0') || (llen <= 0))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// skip over the '%'
 | 
				
			||||||
 | 
							fmt++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// reset formatting state
 | 
				
			||||||
 | 
							flags = 0;
 | 
				
			||||||
 | 
							width = 0;
 | 
				
			||||||
 | 
							prec = -1;
 | 
				
			||||||
 | 
							sign = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rflag:
 | 
				
			||||||
 | 
							ch = *fmt++;
 | 
				
			||||||
 | 
					reswitch:
 | 
				
			||||||
 | 
							switch(ch)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							case '-':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									flags |= LADJUST;
 | 
				
			||||||
 | 
									goto rflag;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '.':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									n = 0;
 | 
				
			||||||
 | 
									while(is_digit((ch = *fmt++)))
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										n = 10 * n + (ch - '0');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									prec = (n < 0) ? -1 : n;
 | 
				
			||||||
 | 
									goto reswitch;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '0':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									flags |= ZEROPAD;
 | 
				
			||||||
 | 
									goto rflag;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '1':
 | 
				
			||||||
 | 
							case '2':
 | 
				
			||||||
 | 
							case '3':
 | 
				
			||||||
 | 
							case '4':
 | 
				
			||||||
 | 
							case '5':
 | 
				
			||||||
 | 
							case '6':
 | 
				
			||||||
 | 
							case '7':
 | 
				
			||||||
 | 
							case '8':
 | 
				
			||||||
 | 
							case '9':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									n = 0;
 | 
				
			||||||
 | 
									do
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										n = 10 * n + (ch - '0');
 | 
				
			||||||
 | 
										ch = *fmt++;
 | 
				
			||||||
 | 
									} while(is_digit(ch));
 | 
				
			||||||
 | 
									width = n;
 | 
				
			||||||
 | 
									goto reswitch;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'c':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (!llen)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										goto done;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									char *c = (char *)args[arg];
 | 
				
			||||||
 | 
									*buf_p++ = *c;
 | 
				
			||||||
 | 
									llen--;
 | 
				
			||||||
 | 
									arg++;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'd':
 | 
				
			||||||
 | 
							case 'i':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									int *value = (int *)args[arg];
 | 
				
			||||||
 | 
									AddInt(&buf_p, llen, *value, width, flags);
 | 
				
			||||||
 | 
									arg++;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'u':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									unsigned int *value = (unsigned int *)args[arg];
 | 
				
			||||||
 | 
									AddUInt(&buf_p, llen, *value, width, flags);
 | 
				
			||||||
 | 
									arg++;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 'f':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									float *value = (float *)args[arg];
 | 
				
			||||||
 | 
									AddFloat(&buf_p, llen, *value, width, prec);
 | 
				
			||||||
 | 
									arg++;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case 's':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									const char *str = (const char *)args[arg];
 | 
				
			||||||
 | 
									AddString(&buf_p, llen, str, width, prec);
 | 
				
			||||||
 | 
									arg++;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '%':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (!llen)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										goto done;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									*buf_p++ = ch;
 | 
				
			||||||
 | 
									llen--;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case '\0':
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (!llen)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										goto done;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									*buf_p++ = '%';
 | 
				
			||||||
 | 
									llen--;
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (!llen)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										goto done;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									*buf_p++ = ch;
 | 
				
			||||||
 | 
									llen--;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						*buf_p = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (maxlen - llen - 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
 | 
					size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (!buffer || !maxlen)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int arg;
 | 
						int arg;
 | 
				
			||||||
	//int args = params[0] / sizeof(cell); //:TODO: wrong, i think params[0] has now the param count not the byte count
 | 
						//int args = params[0] / sizeof(cell); //:TODO: wrong, i think params[0] has now the param count not the byte count
 | 
				
			||||||
	// either way this is only used when the above macro is fixed, until then not needed
 | 
						// either way this is only used when the above macro is fixed, until then not needed
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ using namespace SourcePawn;
 | 
				
			|||||||
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param);
 | 
					size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param);
 | 
				
			||||||
const char *stristr(const char *str, const char *substr);
 | 
					const char *stristr(const char *str, const char *substr);
 | 
				
			||||||
unsigned int strncopy(char *dest, const char *src, size_t count);
 | 
					unsigned int strncopy(char *dest, const char *src, size_t count);
 | 
				
			||||||
 | 
					size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args);
 | 
				
			||||||
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
 | 
					size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_
 | 
					#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_
 | 
				
			||||||
 | 
				
			|||||||
@ -150,12 +150,6 @@ void SourceModBase::StartSourceMod(bool late)
 | 
				
			|||||||
		pBase->OnSourceModAllInitialized();
 | 
							pBase->OnSourceModAllInitialized();
 | 
				
			||||||
		pBase = pBase->m_pGlobalClassNext;
 | 
							pBase = pBase->m_pGlobalClassNext;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If we're late, automatically load plugins now */
 | 
					 | 
				
			||||||
	if (late)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		DoGlobalPluginLoads();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
 | 
					bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user