extensions/clientprefs/extension.cpp
author Matt Woodrow <pred@alliedmods.net>
Tue Aug 05 08:00:54 2008 +0000 (2008-08-05)
changeset 2220 20b404790b3b
parent 2207 3a97a56ded62
child 2338 b9a152a270c9
permissions -rw-r--r--
Increased Clientprefs error reporting and a rare leak on database connection failed.
pred@2009
     1
/**
pred@2009
     2
 * vim: set ts=4 :
pred@2009
     3
 * =============================================================================
pred@2009
     4
 * SourceMod Client Preferences Extension
pred@2009
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
pred@2009
     6
 * =============================================================================
pred@2009
     7
 *
pred@2009
     8
 * This program is free software; you can redistribute it and/or modify it under
pred@2009
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
pred@2009
    10
 * Free Software Foundation.
pred@2009
    11
 * 
pred@2009
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
pred@2009
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@2009
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
pred@2009
    15
 * details.
pred@2009
    16
 *
pred@2009
    17
 * You should have received a copy of the GNU General Public License along with
pred@2009
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
pred@2009
    19
 *
pred@2009
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
pred@2009
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@2009
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@2009
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
pred@2009
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
pred@2009
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
pred@2009
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@2009
    27
 * or <http://www.sourcemod.net/license.php>.
pred@2009
    28
 *
pred@2009
    29
 * Version: $Id$
pred@2009
    30
 */
pred@2009
    31
pred@2009
    32
#include "extension.h"
pred@2009
    33
pred@2009
    34
/**
pred@2009
    35
 * @file extension.cpp
pred@2009
    36
 * @brief Implement extension code here.
pred@2009
    37
 */
pred@2009
    38
pred@2009
    39
ClientPrefs g_ClientPrefs;		/**< Global singleton for extension's main interface */
pred@2009
    40
pred@2009
    41
SMEXT_LINK(&g_ClientPrefs);
pred@2009
    42
pred@2009
    43
HandleType_t g_CookieType = 0;
pred@2009
    44
CookieTypeHandler g_CookieTypeHandler;
pred@2009
    45
pred@2048
    46
HandleType_t g_CookieIterator = 0;
pred@2048
    47
CookieIteratorHandler g_CookieIteratorHandler;
pred@2048
    48
pred@2009
    49
pred@2009
    50
int driver = 0;
pred@2009
    51
pred@2009
    52
bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
pred@2207
    53
{
pred@2207
    54
	queryMutex = threader->MakeMutex();
pred@2207
    55
	cookieMutex = threader->MakeMutex();
pred@2207
    56
pred@2207
    57
	DBInfo = dbi->FindDatabaseConf("clientprefs");
pred@2009
    58
pred@2009
    59
	if (DBInfo == NULL)
pred@2009
    60
	{
pred@2009
    61
		DBInfo = dbi->FindDatabaseConf("default");
pred@2009
    62
pred@2009
    63
		if (DBInfo == NULL)
pred@2009
    64
		{
pred@2009
    65
			snprintf(error, maxlength, "Could not find \"clientprefs\" or \"default\" database configs");
pred@2009
    66
			return false;
pred@2009
    67
		}
pred@2009
    68
	}
pred@2009
    69
pred@2009
    70
	if (DBInfo->driver[0] != '\0')
pred@2009
    71
	{
pred@2009
    72
		Driver = dbi->FindOrLoadDriver(DBInfo->driver);
pred@2009
    73
	}
pred@2009
    74
	else
pred@2009
    75
	{
pred@2009
    76
		Driver = dbi->GetDefaultDriver();
pred@2009
    77
	}
pred@2009
    78
pred@2009
    79
	if (Driver == NULL)
pred@2009
    80
	{
pred@2009
    81
		snprintf(error, maxlength, "Could not load DB Driver \"%s\"", DBInfo->driver);
pred@2009
    82
		return false;
pred@2009
    83
	}
pred@2009
    84
pred@2207
    85
	Database = NULL;
pred@2207
    86
	databaseLoading = true;
pred@2207
    87
	TQueryOp *op = new TQueryOp(Query_Connect, 0);
pred@2207
    88
	dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2009
    89
dvander@2135
    90
	dbi->AddDependency(myself, Driver);
dvander@2135
    91
pred@2009
    92
	sharesys->AddNatives(myself, g_ClientPrefNatives);
pred@2009
    93
	sharesys->RegisterLibrary(myself, "clientprefs");
pred@2048
    94
	g_CookieManager.cookieDataLoadedForward = forwards->CreateForward("OnClientCookiesCached", ET_Ignore, 1, NULL, Param_Cell);
pred@2009
    95
pred@2009
    96
	g_CookieType = handlesys->CreateType("Cookie", 
pred@2009
    97
		&g_CookieTypeHandler, 
pred@2009
    98
		0, 
pred@2009
    99
		NULL, 
pred@2009
   100
		NULL, 
pred@2009
   101
		myself->GetIdentity(), 
pred@2009
   102
		NULL);
pred@2009
   103
pred@2048
   104
	g_CookieIterator = handlesys->CreateType("CookieIterator", 
pred@2048
   105
		&g_CookieIteratorHandler, 
pred@2048
   106
		0, 
pred@2048
   107
		NULL, 
pred@2048
   108
		NULL, 
pred@2048
   109
		myself->GetIdentity(), 
pred@2048
   110
		NULL);
pred@2048
   111
pred@2048
   112
	IMenuStyle *style = menus->GetDefaultStyle();
pred@2048
   113
	g_CookieManager.clientMenu = style->CreateMenu(&g_Handler, NULL);
pred@2048
   114
	g_CookieManager.clientMenu->SetDefaultTitle("Client Settings:");
pred@2048
   115
pred@2048
   116
	plsys->AddPluginsListener(&g_CookieManager);
pred@2048
   117
pred@2048
   118
	phrases = translator->CreatePhraseCollection();
pred@2048
   119
	phrases->AddPhraseFile("clientprefs.phrases");
pred@2048
   120
	phrases->AddPhraseFile("common.phrases");
pred@2048
   121
pred@2009
   122
	return true;
pred@2009
   123
}
pred@2009
   124
pred@2009
   125
void ClientPrefs::SDK_OnAllLoaded()
pred@2009
   126
{
pred@2009
   127
	playerhelpers->AddClientListener(&g_CookieManager);
pred@2009
   128
}
pred@2009
   129
dvander@2135
   130
bool ClientPrefs::QueryInterfaceDrop(SMInterface *pInterface)
dvander@2135
   131
{
dvander@2135
   132
	if ((void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   133
	{
dvander@2135
   134
		return false;
dvander@2135
   135
	}
dvander@2135
   136
dvander@2135
   137
	return true;
dvander@2135
   138
}
dvander@2135
   139
dvander@2135
   140
void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
dvander@2135
   141
{
dvander@2135
   142
	if (Database != NULL && (void *)pInterface == (void *)(Database->GetDriver()))
dvander@2135
   143
	{
pred@2207
   144
		InsertCookieQuery->Destroy();
pred@2207
   145
		SelectDataQuery->Destroy();
pred@2207
   146
		SelectIdQuery->Destroy();
pred@2207
   147
		InsertDataQuery->Destroy();
pred@2207
   148
pred@2207
   149
		InsertCookieQuery = NULL;
pred@2207
   150
		SelectDataQuery = NULL;
pred@2207
   151
		SelectIdQuery = NULL;
pred@2207
   152
		InsertDataQuery = NULL;
pred@2207
   153
dvander@2135
   154
		Database->Close();
dvander@2135
   155
		Database = NULL;
dvander@2135
   156
	}
dvander@2135
   157
}
dvander@2135
   158
pred@2009
   159
void ClientPrefs::SDK_OnUnload()
pred@2009
   160
{
pred@2009
   161
	handlesys->RemoveType(g_CookieType, myself->GetIdentity());
pred@2131
   162
	handlesys->RemoveType(g_CookieIterator, myself->GetIdentity());
pred@2009
   163
pred@2009
   164
	g_CookieManager.Unload();
pred@2009
   165
dvander@2135
   166
	if (Database != NULL)
dvander@2135
   167
	{
dvander@2135
   168
		Database->Close();
dvander@2135
   169
	}
pred@2009
   170
pred@2048
   171
	forwards->ReleaseForward(g_CookieManager.cookieDataLoadedForward);
pred@2048
   172
pred@2048
   173
	g_CookieManager.clientMenu->Destroy();
pred@2131
   174
pred@2131
   175
	phrases->Destroy();
pred@2131
   176
pred@2131
   177
	plsys->RemovePluginsListener(&g_CookieManager);
pred@2131
   178
	playerhelpers->RemoveClientListener(&g_CookieManager);
pred@2207
   179
pred@2207
   180
	/* Kill all our prepared queries - Queries are guaranteed to be flushed before this is called */
pred@2207
   181
pred@2207
   182
	if (InsertCookieQuery != NULL)
pred@2207
   183
	{
pred@2207
   184
		InsertCookieQuery->Destroy();
pred@2207
   185
	}
pred@2207
   186
pred@2207
   187
	if (SelectDataQuery != NULL)
pred@2207
   188
	{
pred@2207
   189
		SelectDataQuery->Destroy();
pred@2207
   190
	}
pred@2207
   191
pred@2207
   192
	if (SelectIdQuery != NULL)
pred@2207
   193
	{
pred@2207
   194
		SelectIdQuery->Destroy();
pred@2207
   195
	}
pred@2207
   196
pred@2207
   197
	if (InsertDataQuery != NULL)
pred@2207
   198
	{
pred@2207
   199
		InsertDataQuery->Destroy();
pred@2207
   200
	}
pred@2207
   201
pred@2207
   202
	queryMutex->DestroyThis();
pred@2207
   203
	cookieMutex->DestroyThis();
pred@2207
   204
}
pred@2207
   205
pred@2207
   206
void ClientPrefs::DatabaseConnect()
pred@2207
   207
{
pred@2207
   208
	char error[256];
pred@2207
   209
	int errCode = 0;
pred@2207
   210
pred@2207
   211
	Database = Driver->Connect(DBInfo, true, error, sizeof(error));
pred@2207
   212
pred@2207
   213
	if (Database == NULL)
pred@2207
   214
	{
pred@2207
   215
		g_pSM->LogError(myself, error);
pred@2207
   216
		databaseLoading = false;
pred@2207
   217
		ProcessQueryCache();
pred@2207
   218
		return;
pred@2207
   219
	}
pred@2207
   220
pred@2207
   221
	const char *identifier = Driver->GetIdentifier();
pred@2207
   222
pred@2207
   223
	if (strcmp(identifier, "sqlite") == 0)
pred@2207
   224
	{
pred@2207
   225
		driver = DRIVER_SQLITE;
pred@2207
   226
pred@2207
   227
		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   228
pred@2207
   229
		op->SetDatabase(Database);
pred@2220
   230
		IPreparedQuery *pQuery = Database->PrepareQuery(
pred@2207
   231
				"CREATE TABLE IF NOT EXISTS sm_cookies  \
pred@2207
   232
				( \
pred@2207
   233
					id INTEGER PRIMARY KEY AUTOINCREMENT, \
pred@2207
   234
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   235
					description varchar(255), \
pred@2207
   236
					access INTEGER \
pred@2207
   237
				)",
pred@2220
   238
				error, sizeof(error), &errCode);
pred@2220
   239
pred@2220
   240
		if (pQuery == NULL)
pred@2220
   241
		{
pred@2220
   242
			g_pSM->LogMessage(myself, "Failed to prepare query CreateTable sm_cookies: %s (%i)", error, errCode);
pred@2220
   243
			return;
pred@2220
   244
		}
pred@2220
   245
pred@2220
   246
		op->SetCustomPreparedQuery(pQuery);
pred@2207
   247
pred@2207
   248
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   249
pred@2207
   250
		op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   251
		op->SetDatabase(Database);
pred@2220
   252
pred@2220
   253
		pQuery = Database->PrepareQuery(
pred@2207
   254
				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
pred@2207
   255
				( \
pred@2207
   256
					player varchar(65) NOT NULL, \
pred@2207
   257
					cookie_id int(10) NOT NULL, \
pred@2207
   258
					value varchar(100), \
pred@2207
   259
					timestamp int, \
pred@2207
   260
					PRIMARY KEY (player, cookie_id) \
pred@2207
   261
				)",
pred@2220
   262
				error, sizeof(error), &errCode);
pred@2220
   263
pred@2220
   264
		if (pQuery == NULL)
pred@2220
   265
		{
pred@2220
   266
			g_pSM->LogMessage(myself, "Failed to prepare query CreateTable sm_cookie_cache: %s (%i)", error, errCode);
pred@2220
   267
			return;
pred@2220
   268
		}
pred@2220
   269
pred@2220
   270
		op->SetCustomPreparedQuery(pQuery);
pred@2207
   271
pred@2207
   272
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   273
	}
pred@2207
   274
	else if (strcmp(identifier, "mysql") == 0)
pred@2207
   275
	{
pred@2207
   276
		driver = DRIVER_MYSQL;
pred@2207
   277
pred@2207
   278
		TQueryOp *op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   279
		op->SetDatabase(Database);
pred@2220
   280
		
pred@2220
   281
		IPreparedQuery *pQuery = Database->PrepareQuery(
pred@2207
   282
				"CREATE TABLE IF NOT EXISTS sm_cookies \
pred@2207
   283
				( \
pred@2207
   284
					id INTEGER unsigned NOT NULL auto_increment, \
pred@2207
   285
					name varchar(30) NOT NULL UNIQUE, \
pred@2207
   286
					description varchar(255), \
pred@2207
   287
					access INTEGER, \
pred@2207
   288
					PRIMARY KEY (id) \
pred@2207
   289
				)",
pred@2220
   290
				error, sizeof(error), &errCode);
pred@2220
   291
pred@2220
   292
		if (pQuery == NULL)
pred@2220
   293
		{
pred@2220
   294
			g_pSM->LogMessage(myself, "Failed to prepare query CreateTable sm_cookies: %s (%i)", error, errCode);
pred@2220
   295
			return;
pred@2220
   296
		}
pred@2220
   297
pred@2220
   298
		op->SetCustomPreparedQuery(pQuery);
pred@2207
   299
pred@2207
   300
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   301
pred@2207
   302
		op = new TQueryOp(Query_CreateTable, 0);
pred@2207
   303
		op->SetDatabase(Database);
pred@2220
   304
			
pred@2220
   305
		pQuery = Database->PrepareQuery(
pred@2207
   306
				"CREATE TABLE IF NOT EXISTS sm_cookie_cache \
pred@2207
   307
				( \
pred@2207
   308
					player varchar(65) NOT NULL, \
pred@2207
   309
					cookie_id int(10) NOT NULL, \
pred@2207
   310
					value varchar(100), \
pred@2207
   311
					timestamp int NOT NULL, \
pred@2207
   312
					PRIMARY KEY (player, cookie_id) \
pred@2207
   313
				)",
pred@2220
   314
				error, sizeof(error), &errCode);
pred@2220
   315
pred@2220
   316
		if (pQuery == NULL)
pred@2220
   317
		{
pred@2220
   318
			g_pSM->LogMessage(myself, "Failed to prepare query CreateTable sm_cookie_cache: %s (%i)", error, errCode);
pred@2220
   319
			return;
pred@2220
   320
		}
pred@2220
   321
pred@2220
   322
		op->SetCustomPreparedQuery(pQuery);
pred@2207
   323
pred@2207
   324
		dbi->AddToThreadQueue(op, PrioQueue_High);
pred@2207
   325
	}
pred@2207
   326
	else
pred@2207
   327
	{
pred@2207
   328
		g_pSM->LogError(myself, "Unsupported driver \"%s\"", identifier);
pred@2207
   329
		Database->Close();
pred@2207
   330
		Database = NULL;
pred@2207
   331
		databaseLoading = false;
pred@2207
   332
		ProcessQueryCache();
pred@2207
   333
		return;
pred@2207
   334
	}
pred@2207
   335
pred@2207
   336
	if (driver == DRIVER_MYSQL)
pred@2207
   337
	{
pred@2207
   338
		InsertCookieQuery = Database->PrepareQuery(
pred@2207
   339
				"INSERT IGNORE INTO sm_cookies(name, description, access) \
pred@2207
   340
				VALUES(?, ?, ?)",
pred@2207
   341
				error, sizeof(error), &errCode);
pred@2220
   342
pred@2220
   343
		if (InsertCookieQuery == NULL)
pred@2220
   344
		{
pred@2220
   345
			g_pSM->LogMessage(myself, "Failed to prepare query InsertCookie: %s (%i)", error, errCode);
pred@2220
   346
			return;
pred@2220
   347
		}
pred@2220
   348
pred@2207
   349
		InsertDataQuery = Database->PrepareQuery(
pred@2207
   350
				"INSERT INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
pred@2207
   351
				VALUES(?, ?, ?, ?) \
pred@2207
   352
				ON DUPLICATE KEY UPDATE value = ?, timestamp = ?",
pred@2207
   353
				error, sizeof(error), &errCode);
pred@2220
   354
pred@2220
   355
		if (InsertDataQuery == NULL)
pred@2220
   356
		{
pred@2220
   357
			g_pSM->LogMessage(myself, "Failed to prepare query InsertData: %s (%i)", error, errCode);
pred@2220
   358
			return;
pred@2220
   359
		}
pred@2207
   360
	}
pred@2207
   361
	else
pred@2207
   362
	{
pred@2207
   363
		InsertCookieQuery = Database->PrepareQuery(
pred@2207
   364
				"INSERT OR IGNORE INTO sm_cookies(name, description, access) \
pred@2207
   365
				VALUES(?, ?, ?)", 
pred@2207
   366
				error, sizeof(error), &errCode);
pred@2220
   367
pred@2220
   368
		if (InsertCookieQuery == NULL)
pred@2220
   369
		{
pred@2220
   370
			g_pSM->LogMessage(myself, "Failed to prepare query InsertCookie: %s (%i)", error, errCode);
pred@2220
   371
			return;
pred@2220
   372
		}
pred@2220
   373
pred@2207
   374
		InsertDataQuery = Database->PrepareQuery(
pred@2207
   375
				"INSERT OR REPLACE INTO sm_cookie_cache(player, cookie_id, value, timestamp) \
pred@2207
   376
				VALUES(?, ?, ?, ?)", 
pred@2207
   377
				error, sizeof(error), &errCode);
pred@2220
   378
pred@2220
   379
		if (InsertDataQuery == NULL)
pred@2220
   380
		{
pred@2220
   381
			g_pSM->LogMessage(myself, "Failed to prepare query InsertData: %s (%i)", error, errCode);
pred@2220
   382
			return;
pred@2220
   383
		}
pred@2207
   384
	}
pred@2207
   385
pred@2207
   386
	SelectDataQuery = Database->PrepareQuery(
pred@2207
   387
			"SELECT sm_cookies.name, sm_cookie_cache.value, sm_cookies.description, sm_cookies.access \
pred@2207
   388
			FROM sm_cookies \
pred@2207
   389
			JOIN sm_cookie_cache \
pred@2207
   390
			ON sm_cookies.id = sm_cookie_cache.cookie_id \
pred@2207
   391
			WHERE player = ?",
pred@2207
   392
			error, sizeof(error), &errCode);
pred@2207
   393
pred@2220
   394
	if (SelectDataQuery == NULL)
pred@2220
   395
	{
pred@2220
   396
		g_pSM->LogMessage(myself, "Failed to prepare query SelectData: %s (%i)", error, errCode);
pred@2220
   397
		return;
pred@2220
   398
	}
pred@2220
   399
pred@2207
   400
	SelectIdQuery = Database->PrepareQuery(
pred@2207
   401
			"SELECT id \
pred@2207
   402
			FROM sm_cookies \
pred@2207
   403
			WHERE name=?",
pred@2207
   404
			error, sizeof(error), &errCode);
pred@2207
   405
pred@2220
   406
	if (SelectIdQuery == NULL)
pred@2220
   407
	{
pred@2220
   408
		g_pSM->LogMessage(myself, "Failed to prepare query SelectId: %s (%i)", error, errCode);
pred@2220
   409
		return;
pred@2220
   410
	}
pred@2220
   411
pred@2207
   412
	databaseLoading = false;
pred@2207
   413
	cell_t result = 0;
pred@2207
   414
pred@2207
   415
	ProcessQueryCache();
pred@2207
   416
pred@2207
   417
	return;
pred@2207
   418
}
pred@2207
   419
pred@2207
   420
bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
pred@2207
   421
{
pred@2207
   422
	queryMutex->Lock();
pred@2207
   423
pred@2207
   424
	if (Database == NULL && databaseLoading)
pred@2207
   425
	{
pred@2207
   426
		cachedQueries.push_back(query);
pred@2207
   427
		queryMutex->Unlock();
pred@2207
   428
		return true;
pred@2207
   429
	}
pred@2207
   430
pred@2207
   431
	queryMutex->Unlock();
pred@2207
   432
pred@2207
   433
	if (Database)
pred@2207
   434
	{
pred@2207
   435
		query->SetDatabase(Database);
pred@2207
   436
		query->SetPreparedQuery();
pred@2207
   437
		dbi->AddToThreadQueue(query, PrioQueue_Normal);
pred@2207
   438
		return true;
pred@2207
   439
	}
pred@2207
   440
pred@2220
   441
	query->Destroy();
pred@2220
   442
pred@2207
   443
	/* If Database is NULL and we're not in the loading phase it must have failed - Can't do much */
pred@2207
   444
	return false;
pred@2207
   445
}
pred@2207
   446
pred@2207
   447
void ClientPrefs::ProcessQueryCache()
pred@2207
   448
{
pred@2207
   449
	SourceHook::List<TQueryOp *>::iterator iter;
pred@2207
   450
pred@2207
   451
	queryMutex->Lock();
pred@2207
   452
pred@2207
   453
	iter = cachedQueries.begin();
pred@2207
   454
	
pred@2207
   455
	while (iter != cachedQueries.end())
pred@2207
   456
	{
pred@2207
   457
		TQueryOp *op = (TQueryOp *)*iter;
pred@2207
   458
pred@2207
   459
		if (Database != NULL)
pred@2207
   460
		{
pred@2207
   461
			op->SetDatabase(Database);
pred@2207
   462
			op->SetPreparedQuery();
pred@2207
   463
			dbi->AddToThreadQueue(op, PrioQueue_Normal);
pred@2207
   464
		}
pred@2207
   465
		else
pred@2207
   466
		{
pred@2207
   467
			delete op;
pred@2207
   468
		}
pred@2207
   469
pred@2207
   470
		iter++;
pred@2207
   471
	}
pred@2207
   472
pred@2207
   473
	cachedQueries.clear();
pred@2207
   474
pred@2207
   475
	queryMutex->Unlock();
pred@2048
   476
}
pred@2048
   477
pred@2048
   478
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
pred@2048
   479
{
pred@2048
   480
	va_list ap;
pred@2048
   481
	va_start(ap, fmt);
pred@2048
   482
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
pred@2048
   483
	va_end(ap);
pred@2048
   484
pred@2048
   485
	if (len >= maxlength)
pred@2048
   486
	{
pred@2048
   487
		buffer[maxlength - 1] = '\0';
pred@2048
   488
		return (maxlength - 1);
pred@2048
   489
	}
pred@2048
   490
	else
pred@2048
   491
	{
pred@2048
   492
		return len;
pred@2048
   493
	}
pred@2048
   494
}
pred@2048
   495
pred@2048
   496
bool Translate(char *buffer, 
pred@2048
   497
				   size_t maxlength,
pred@2048
   498
				   const char *format,
pred@2048
   499
				   unsigned int numparams,
pred@2048
   500
				   size_t *pOutLength,
pred@2048
   501
				   ...)
pred@2048
   502
{
pred@2048
   503
	va_list ap;
pred@2048
   504
	unsigned int i;
pred@2048
   505
	const char *fail_phrase;
pred@2048
   506
	void *params[MAX_TRANSLATE_PARAMS];
pred@2048
   507
pred@2048
   508
	if (numparams > MAX_TRANSLATE_PARAMS)
pred@2048
   509
	{
pred@2048
   510
		assert(false);
pred@2048
   511
		return false;
pred@2048
   512
	}
pred@2048
   513
pred@2048
   514
	va_start(ap, pOutLength);
pred@2048
   515
	for (i = 0; i < numparams; i++)
pred@2048
   516
	{
pred@2048
   517
		params[i] = va_arg(ap, void *);
pred@2048
   518
	}
pred@2048
   519
	va_end(ap);
pred@2048
   520
pred@2048
   521
	if (!g_ClientPrefs.phrases->FormatString(buffer,
pred@2048
   522
		maxlength, 
pred@2048
   523
		format, 
pred@2048
   524
		params,
pred@2048
   525
		numparams,
pred@2048
   526
		pOutLength,
pred@2048
   527
		&fail_phrase))
pred@2048
   528
	{
pred@2048
   529
		if (fail_phrase != NULL)
pred@2048
   530
		{
pred@2048
   531
			g_pSM->LogError(myself, "[SM] Could not find core phrase: %s", fail_phrase);
pred@2048
   532
		}
pred@2048
   533
		else
pred@2048
   534
		{
pred@2048
   535
			g_pSM->LogError(myself, "[SM] Unknown fatal error while translating a core phrase.");
pred@2048
   536
		}
pred@2048
   537
pred@2048
   538
		return false;
pred@2048
   539
	}
pred@2048
   540
pred@2048
   541
	return true;
pred@2009
   542
}
pred@2009
   543
pred@2009
   544
pred@2048
   545