core/systems/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jan 18 00:17:36 2007 +0000 (2007-01-18)
changeset 310 1efceafbda26
parent 305 59ef1afbe958
child 311 31192ed0fe13
permissions -rw-r--r--
added [SM] to direct responses of menu cmds
began adding the extension menu
dvander@298
     1
#include "ExtensionSys.h"
dvander@298
     2
#include "LibrarySys.h"
dvander@298
     3
#include "ShareSys.h"
dvander@298
     4
#include "CLogger.h"
dvander@304
     5
#include "sourcemm_api.h"
dvander@305
     6
#include "PluginSys.h"
dvander@310
     7
#include "sm_srvcmds.h"
dvander@298
     8
dvander@298
     9
CExtensionManager g_Extensions;
dvander@298
    10
IdentityType_t g_ExtType;
dvander@298
    11
dvander@298
    12
CExtension::CExtension(const char *filename, char *error, size_t err_max)
dvander@298
    13
{
dvander@298
    14
	m_File.assign(filename);
dvander@298
    15
	m_pAPI = NULL;
dvander@298
    16
	m_pIdentToken = NULL;
dvander@304
    17
	m_PlId = 0;
dvander@298
    18
dvander@298
    19
	char path[PLATFORM_MAX_PATH+1];
dvander@298
    20
	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename);
dvander@298
    21
dvander@298
    22
	m_pLib = g_LibSys.OpenLibrary(path, error, err_max);
dvander@298
    23
dvander@298
    24
	if (m_pLib == NULL)
dvander@298
    25
	{
dvander@298
    26
		return;
dvander@298
    27
	}
dvander@298
    28
dvander@298
    29
	typedef IExtensionInterface *(*GETAPI)();
dvander@298
    30
	GETAPI pfnGetAPI = NULL;
dvander@298
    31
dvander@298
    32
	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
dvander@298
    33
	{
dvander@298
    34
		m_pLib->CloseLibrary();
dvander@298
    35
		m_pLib = NULL;
dvander@298
    36
		snprintf(error, err_max, "Unable to find extension entry point");
dvander@298
    37
		return;
dvander@298
    38
	}
dvander@298
    39
dvander@298
    40
	m_pAPI = pfnGetAPI();
dvander@298
    41
	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
dvander@298
    42
	{
dvander@298
    43
		m_pLib->CloseLibrary();
dvander@298
    44
		m_pLib = NULL;
dvander@298
    45
		snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
dvander@298
    46
		return;
dvander@298
    47
	}
dvander@298
    48
dvander@298
    49
	if (m_pAPI->IsMetamodExtension())
dvander@298
    50
	{
dvander@304
    51
		bool already;
dvander@304
    52
		m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max);
dvander@298
    53
	}
dvander@298
    54
dvander@298
    55
	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
dvander@298
    56
dvander@298
    57
	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, g_SourceMod.IsLateLoadInMap()))
dvander@298
    58
	{
dvander@298
    59
		if (m_pAPI->IsMetamodExtension())
dvander@298
    60
		{
dvander@304
    61
			if (m_PlId)
dvander@304
    62
			{
dvander@304
    63
				char dummy[255];
dvander@304
    64
				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
dvander@304
    65
				m_PlId = 0;
dvander@304
    66
			}
dvander@298
    67
		}
dvander@298
    68
		m_pAPI = NULL;
dvander@298
    69
		m_pLib->CloseLibrary();
dvander@298
    70
		m_pLib = NULL;
dvander@298
    71
		g_ShareSys.DestroyIdentity(m_pIdentToken);
dvander@298
    72
		m_pIdentToken = NULL;
dvander@298
    73
		return;
dvander@298
    74
	}
dvander@298
    75
}
dvander@298
    76
dvander@298
    77
CExtension::~CExtension()
dvander@298
    78
{
dvander@298
    79
	if (m_pAPI)
dvander@298
    80
	{
dvander@298
    81
		m_pAPI->OnExtensionUnload();
dvander@304
    82
		if (m_PlId)
dvander@304
    83
		{
dvander@304
    84
			g_pMMPlugins->Unload(m_PlId, true, NULL, 0);
dvander@304
    85
		}
dvander@298
    86
	}
dvander@298
    87
dvander@298
    88
	if (m_pIdentToken)
dvander@298
    89
	{
dvander@298
    90
		g_ShareSys.DestroyIdentity(m_pIdentToken);
dvander@298
    91
	}
dvander@298
    92
dvander@298
    93
	if (m_pLib)
dvander@298
    94
	{
dvander@298
    95
		m_pLib->CloseLibrary();
dvander@298
    96
	}
dvander@298
    97
}
dvander@298
    98
dvander@305
    99
void CExtension::AddPlugin(IPlugin *pPlugin)
dvander@305
   100
{
dvander@305
   101
	m_Plugins.push_back(pPlugin);
dvander@305
   102
}
dvander@305
   103
dvander@305
   104
void CExtension::RemovePlugin(IPlugin *pPlugin)
dvander@305
   105
{
dvander@305
   106
	m_Plugins.remove(pPlugin);
dvander@305
   107
}
dvander@305
   108
dvander@298
   109
void CExtension::SetError(const char *error)
dvander@298
   110
{
dvander@298
   111
	m_Error.assign(error);
dvander@298
   112
}
dvander@298
   113
dvander@298
   114
IExtensionInterface *CExtension::GetAPI()
dvander@298
   115
{
dvander@298
   116
	return m_pAPI;
dvander@298
   117
}
dvander@298
   118
dvander@298
   119
const char *CExtension::GetFilename()
dvander@298
   120
{
dvander@298
   121
	return m_File.c_str();
dvander@298
   122
}
dvander@298
   123
dvander@298
   124
IdentityToken_t *CExtension::GetIdentity()
dvander@298
   125
{
dvander@298
   126
	return m_pIdentToken;
dvander@298
   127
}
dvander@298
   128
dvander@298
   129
bool CExtension::IsLoaded()
dvander@298
   130
{
dvander@298
   131
	return (m_pLib != NULL);
dvander@298
   132
}
dvander@298
   133
dvander@304
   134
void CExtension::AddDependency(IfaceInfo *pInfo)
dvander@304
   135
{
dvander@304
   136
	m_Deps.push_back(*pInfo);
dvander@304
   137
}
dvander@304
   138
dvander@304
   139
ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
dvander@304
   140
{
dvander@304
   141
	List<IfaceInfo>::iterator iter = m_Deps.begin();
dvander@304
   142
dvander@304
   143
	if (iter == m_Deps.end())
dvander@304
   144
	{
dvander@304
   145
		return NULL;
dvander@304
   146
	}
dvander@304
   147
dvander@304
   148
	if (pOwner)
dvander@304
   149
	{
dvander@304
   150
		*pOwner = (*iter).owner;
dvander@304
   151
	}
dvander@304
   152
	if (pInterface)
dvander@304
   153
	{
dvander@304
   154
		*pInterface = (*iter).iface;
dvander@304
   155
	}
dvander@304
   156
dvander@304
   157
	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
dvander@304
   158
dvander@304
   159
	return (ITERATOR *)pIter;
dvander@304
   160
}
dvander@304
   161
dvander@304
   162
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
dvander@304
   163
{
dvander@304
   164
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
dvander@304
   165
	List<IfaceInfo>::iterator _iter;
dvander@304
   166
dvander@304
   167
	if (_iter == m_Deps.end())
dvander@304
   168
	{
dvander@304
   169
		return false;
dvander@304
   170
	}
dvander@304
   171
dvander@304
   172
	_iter++;
dvander@304
   173
dvander@304
   174
	if (pOwner)
dvander@304
   175
	{
dvander@304
   176
		*pOwner = (*_iter).owner;
dvander@304
   177
	}
dvander@304
   178
	if (pInterface)
dvander@304
   179
	{
dvander@304
   180
		*pInterface = (*_iter).iface;
dvander@304
   181
	}
dvander@304
   182
dvander@304
   183
	*pIter = _iter;
dvander@304
   184
dvander@304
   185
	if (_iter == m_Deps.end())
dvander@304
   186
	{
dvander@304
   187
		return false;
dvander@304
   188
	}
dvander@304
   189
dvander@304
   190
	return true;
dvander@304
   191
}
dvander@304
   192
dvander@304
   193
void CExtension::FreeDependencyIterator(ITERATOR *iter)
dvander@304
   194
{
dvander@304
   195
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
dvander@304
   196
dvander@304
   197
	delete pIter;
dvander@304
   198
}
dvander@304
   199
dvander@304
   200
void CExtension::AddInterface(SMInterface *pInterface)
dvander@304
   201
{
dvander@304
   202
	m_Interfaces.push_back(pInterface);
dvander@304
   203
}
dvander@304
   204
dvander@304
   205
/*********************
dvander@304
   206
 * EXTENSION MANAGER *
dvander@304
   207
 *********************/
dvander@304
   208
dvander@298
   209
void CExtensionManager::OnSourceModAllInitialized()
dvander@298
   210
{
dvander@298
   211
	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
dvander@305
   212
	g_PluginSys.AddPluginsListener(this);
dvander@310
   213
	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
dvander@298
   214
}
dvander@298
   215
dvander@298
   216
void CExtensionManager::OnSourceModShutdown()
dvander@298
   217
{
dvander@310
   218
	g_RootMenu.RemoveRootConsoleCommand("exts", this);
dvander@305
   219
	g_PluginSys.RemovePluginsListener(this);
dvander@298
   220
	g_ShareSys.DestroyIdentType(g_ExtType);
dvander@298
   221
}
dvander@298
   222
dvander@298
   223
IExtension *CExtensionManager::LoadAutoExtension(const char *path)
dvander@298
   224
{
dvander@304
   225
	IExtension *pAlready;
dvander@304
   226
	if ((pAlready=FindExtensionByFile(path)) != NULL)
dvander@304
   227
	{
dvander@304
   228
		return pAlready;
dvander@304
   229
	}
dvander@304
   230
dvander@298
   231
	char error[256];
dvander@298
   232
	CExtension *p = new CExtension(path, error, sizeof(error));
dvander@298
   233
dvander@298
   234
	if (!p->IsLoaded())
dvander@298
   235
	{
dvander@305
   236
		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
dvander@298
   237
		p->SetError(error);
dvander@298
   238
	}
dvander@298
   239
dvander@298
   240
	m_Libs.push_back(p);
dvander@298
   241
dvander@298
   242
	return p;
dvander@298
   243
}
dvander@298
   244
dvander@298
   245
IExtension *CExtensionManager::FindExtensionByFile(const char *file)
dvander@298
   246
{
dvander@298
   247
	List<CExtension *>::iterator iter;
dvander@298
   248
	CExtension *pExt;
dvander@298
   249
dvander@298
   250
	/* Make sure the file direction is right */
dvander@298
   251
	char path[PLATFORM_MAX_PATH+1];
dvander@298
   252
	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
dvander@298
   253
dvander@298
   254
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@298
   255
	{
dvander@298
   256
		pExt = (*iter);
dvander@298
   257
		if (strcmp(pExt->GetFilename(), path) == 0)
dvander@298
   258
		{
dvander@298
   259
			return pExt;
dvander@298
   260
		}
dvander@298
   261
	}
dvander@298
   262
dvander@298
   263
	return NULL;
dvander@298
   264
}
dvander@298
   265
dvander@298
   266
IExtension *CExtensionManager::FindExtensionByName(const char *ext)
dvander@298
   267
{
dvander@298
   268
	List<CExtension *>::iterator iter;
dvander@298
   269
	CExtension *pExt;
dvander@298
   270
	IExtensionInterface *pAPI;
dvander@298
   271
	const char *name;
dvander@298
   272
dvander@298
   273
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@298
   274
	{
dvander@298
   275
		pExt = (*iter);
dvander@298
   276
		if (!pExt->IsLoaded())
dvander@298
   277
		{
dvander@298
   278
			continue;
dvander@298
   279
		}
dvander@298
   280
		if ((pAPI = pExt->GetAPI()) == NULL)
dvander@298
   281
		{
dvander@298
   282
			continue;
dvander@298
   283
		}
dvander@298
   284
		name = pAPI->GetExtensionName();
dvander@298
   285
		if (!name)
dvander@298
   286
		{
dvander@298
   287
			continue;
dvander@298
   288
		}
dvander@298
   289
		if (strcmp(name, ext) == 0)
dvander@298
   290
		{
dvander@298
   291
			return pExt;
dvander@298
   292
		}
dvander@298
   293
	}
dvander@298
   294
dvander@298
   295
	return NULL;
dvander@298
   296
}
dvander@298
   297
dvander@298
   298
IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max)
dvander@298
   299
{
dvander@304
   300
	IExtension *pAlready;
dvander@304
   301
	if ((pAlready=FindExtensionByFile(file)) != NULL)
dvander@304
   302
	{
dvander@304
   303
		return pAlready;
dvander@304
   304
	}
dvander@304
   305
dvander@298
   306
	CExtension *pExt = new CExtension(file, error, err_max);
dvander@298
   307
dvander@298
   308
	/* :NOTE: lifetime is currently ignored */
dvander@298
   309
dvander@298
   310
	if (!pExt->IsLoaded())
dvander@298
   311
	{
dvander@298
   312
		delete pExt;
dvander@298
   313
		return NULL;
dvander@298
   314
	}
dvander@298
   315
dvander@298
   316
	m_Libs.push_back(pExt);
dvander@298
   317
dvander@298
   318
	return pExt;
dvander@298
   319
}
dvander@298
   320
dvander@304
   321
void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo)
dvander@298
   322
{
dvander@304
   323
	CExtension *pExt = (CExtension *)pOwner;
dvander@304
   324
dvander@304
   325
	pExt->AddDependency(pInfo);
dvander@304
   326
}
dvander@304
   327
dvander@304
   328
void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
dvander@304
   329
{
dvander@304
   330
	CExtension *pExt = (CExtension *)pOwner;
dvander@304
   331
dvander@304
   332
	pExt->AddInterface(pInterface);
dvander@298
   333
}
dvander@298
   334
dvander@305
   335
void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
dvander@305
   336
{
dvander@305
   337
	CExtension *pExt = (CExtension *)pParent;
dvander@305
   338
dvander@305
   339
	pExt->AddPlugin(pPlugin);
dvander@305
   340
}
dvander@305
   341
dvander@305
   342
void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
dvander@305
   343
{
dvander@305
   344
	List<CExtension *>::iterator iter;
dvander@305
   345
dvander@305
   346
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@305
   347
	{
dvander@305
   348
		(*iter)->RemovePlugin(plugin);
dvander@305
   349
	}
dvander@305
   350
}
dvander@305
   351
dvander@304
   352
bool CExtensionManager::UnloadExtension(IExtension *_pExt)
dvander@298
   353
{
dvander@304
   354
	if (!_pExt)
dvander@304
   355
	{
dvander@304
   356
		return false;
dvander@304
   357
	}
dvander@304
   358
dvander@304
   359
	CExtension *pExt = (CExtension *)_pExt;
dvander@304
   360
dvander@304
   361
	if (m_Libs.find(pExt) == m_Libs.end())
dvander@304
   362
	{
dvander@304
   363
		return false;
dvander@304
   364
	}
dvander@304
   365
dvander@304
   366
	/* First remove us from internal lists */
dvander@304
   367
	g_ShareSys.RemoveInterfaces(_pExt);
dvander@304
   368
	m_Libs.remove(pExt);
dvander@304
   369
dvander@304
   370
	List<CExtension *> UnloadQueue;
dvander@298
   371
dvander@304
   372
	/* Handle dependencies */
dvander@304
   373
	if (pExt->IsLoaded())
dvander@304
   374
	{
dvander@305
   375
		/* Unload any dependent plugins */
dvander@305
   376
		List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
dvander@305
   377
		while (p_iter != pExt->m_Plugins.end())
dvander@305
   378
		{
dvander@305
   379
			g_PluginSys.UnloadPlugin((*p_iter));
dvander@305
   380
			/* It should already have been removed! */
dvander@305
   381
			assert(pExt->m_Plugins.find((*p_iter)) != pExt->m_Plugins.end());
dvander@305
   382
		}
dvander@305
   383
dvander@304
   384
		/* Notify and/or unload all dependencies */
dvander@304
   385
		List<CExtension *>::iterator c_iter;
dvander@304
   386
		CExtension *pDep;
dvander@304
   387
		IExtensionInterface *pAPI;
dvander@304
   388
		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
dvander@304
   389
		{
dvander@304
   390
			pDep = (*c_iter);
dvander@304
   391
			if ((pAPI=pDep->GetAPI()) == NULL)
dvander@304
   392
			{
dvander@304
   393
				continue;
dvander@304
   394
			}
dvander@304
   395
			/* Now, get its dependency list */
dvander@304
   396
			bool dropped = false;
dvander@304
   397
			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
dvander@304
   398
			while (i_iter != pDep->m_Deps.end())
dvander@304
   399
			{
dvander@304
   400
				if ((*i_iter).owner == _pExt)
dvander@304
   401
				{
dvander@304
   402
					if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface))
dvander@304
   403
					{
dvander@304
   404
						dropped = true;
dvander@304
   405
					}
dvander@304
   406
					pAPI->NotifyInterfaceDrop((*i_iter).iface);
dvander@304
   407
					i_iter = pDep->m_Deps.erase(i_iter);
dvander@304
   408
				} else {
dvander@304
   409
					i_iter++;
dvander@304
   410
				}
dvander@304
   411
			}
dvander@304
   412
		}
dvander@304
   413
	}
dvander@304
   414
dvander@304
   415
	List<CExtension *>::iterator iter;
dvander@304
   416
	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
dvander@304
   417
	{
dvander@304
   418
		/* NOTE: This is safe because the unload function backs out of anything not present */
dvander@304
   419
		UnloadExtension((*iter));
dvander@304
   420
	}
dvander@304
   421
dvander@298
   422
	return true;
dvander@298
   423
}
dvander@310
   424
dvander@310
   425
void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argcount)
dvander@310
   426
{
dvander@310
   427
	if (argcount >= 3)
dvander@310
   428
	{
dvander@310
   429
		const char *cmd = g_RootMenu.GetArgument(2);
dvander@310
   430
		if (strcmp(cmd, "list") == 0)
dvander@310
   431
		{
dvander@310
   432
			List<CExtension *>::iterator iter;
dvander@310
   433
			CExtension *pExt;
dvander@310
   434
			unsigned int num = 1;
dvander@310
   435
			switch (m_Libs.size())
dvander@310
   436
			{
dvander@310
   437
			case 1:
dvander@310
   438
				{
dvander@310
   439
					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
dvander@310
   440
					break;
dvander@310
   441
				}
dvander@310
   442
			case 0:
dvander@310
   443
				{
dvander@310
   444
					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
dvander@310
   445
					break;
dvander@310
   446
				}
dvander@310
   447
			default:
dvander@310
   448
				{
dvander@310
   449
					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
dvander@310
   450
					break;
dvander@310
   451
				}
dvander@310
   452
			}
dvander@310
   453
			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
dvander@310
   454
			{
dvander@310
   455
				pExt = (*iter);
dvander@310
   456
				if (pExt->IsLoaded())
dvander@310
   457
				{
dvander@310
   458
					IExtensionInterface *pAPI = pExt->GetAPI();
dvander@310
   459
					const char *name = pAPI->GetExtensionName();
dvander@310
   460
					const char *version = pAPI->GetExtensionVerString();
dvander@310
   461
					const char *descr = pAPI->GetExtensionDescription();
dvander@310
   462
					g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
dvander@310
   463
				} else {
dvander@310
   464
					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
dvander@310
   465
				}
dvander@310
   466
			}
dvander@310
   467
			return;
dvander@310
   468
		} else if (strcmp(cmd, "info") == 0) {
dvander@310
   469
			if (argcount < 4)
dvander@310
   470
			{
dvander@310
   471
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
dvander@310
   472
				return;
dvander@310
   473
			}
dvander@310
   474
dvander@310
   475
			const char *sId = g_RootMenu.GetArgument(3);
dvander@310
   476
			unsigned int id = atoi(sId);
dvander@310
   477
			if (id <= 0)
dvander@310
   478
			{
dvander@310
   479
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
dvander@310
   480
				return;
dvander@310
   481
			}
dvander@310
   482
dvander@310
   483
			if (m_Libs.size() == 0)
dvander@310
   484
			{
dvander@310
   485
				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
dvander@310
   486
				return;
dvander@310
   487
			}
dvander@310
   488
dvander@310
   489
			if (id > m_Libs.size())
dvander@310
   490
			{
dvander@310
   491
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
dvander@310
   492
				return;
dvander@310
   493
			}
dvander@310
   494
dvander@310
   495
			List<CExtension *>::iterator iter = m_Libs.begin();
dvander@310
   496
			CExtension *pExt = NULL;
dvander@310
   497
			while (iter != m_Libs.end())
dvander@310
   498
			{
dvander@310
   499
				if (--id == 0)
dvander@310
   500
				{
dvander@310
   501
					pExt = (*iter);
dvander@310
   502
					break;
dvander@310
   503
				}
dvander@310
   504
				iter++;
dvander@310
   505
			}
dvander@310
   506
			/* This should never happen */
dvander@310
   507
			if (!pExt)
dvander@310
   508
			{
dvander@310
   509
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
dvander@310
   510
				return;
dvander@310
   511
			}
dvander@310
   512
dvander@310
   513
			if (!pExt->IsLoaded())
dvander@310
   514
			{
dvander@310
   515
				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
dvander@310
   516
				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
dvander@310
   517
			} else {
dvander@310
   518
				IExtensionInterface *pAPI = pExt->GetAPI();
dvander@310
   519
				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
dvander@310
   520
				g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
dvander@310
   521
				g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
dvander@310
   522
				g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
dvander@310
   523
				g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
dvander@310
   524
				g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no");
dvander@310
   525
			}
dvander@310
   526
			return;
dvander@310
   527
		}
dvander@310
   528
	}
dvander@310
   529
dvander@310
   530
	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
dvander@310
   531
	g_RootMenu.DrawGenericOption("info", "Extra extension information");
dvander@310
   532
	g_RootMenu.DrawGenericOption("list", "List extensions");
dvander@310
   533
}