core/systems/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jan 18 09:14:20 2007 +0000 (2007-01-18)
changeset 311 31192ed0fe13
parent 310 1efceafbda26
child 315 5f48300aa103
permissions -rw-r--r--
fixed a dep bug
fixed a plugin unload iterator bug
extension unloading now works
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@311
     8
#include <stdlib.h>
dvander@298
     9
dvander@298
    10
CExtensionManager g_Extensions;
dvander@298
    11
IdentityType_t g_ExtType;
dvander@298
    12
dvander@298
    13
CExtension::CExtension(const char *filename, char *error, size_t err_max)
dvander@298
    14
{
dvander@298
    15
	m_File.assign(filename);
dvander@298
    16
	m_pAPI = NULL;
dvander@298
    17
	m_pIdentToken = NULL;
dvander@304
    18
	m_PlId = 0;
dvander@311
    19
	unload_code = 0;
dvander@298
    20
dvander@298
    21
	char path[PLATFORM_MAX_PATH+1];
dvander@298
    22
	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename);
dvander@298
    23
dvander@298
    24
	m_pLib = g_LibSys.OpenLibrary(path, error, err_max);
dvander@298
    25
dvander@298
    26
	if (m_pLib == NULL)
dvander@298
    27
	{
dvander@298
    28
		return;
dvander@298
    29
	}
dvander@298
    30
dvander@298
    31
	typedef IExtensionInterface *(*GETAPI)();
dvander@298
    32
	GETAPI pfnGetAPI = NULL;
dvander@298
    33
dvander@298
    34
	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
dvander@298
    35
	{
dvander@298
    36
		m_pLib->CloseLibrary();
dvander@298
    37
		m_pLib = NULL;
dvander@298
    38
		snprintf(error, err_max, "Unable to find extension entry point");
dvander@298
    39
		return;
dvander@298
    40
	}
dvander@298
    41
dvander@298
    42
	m_pAPI = pfnGetAPI();
dvander@298
    43
	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
dvander@298
    44
	{
dvander@298
    45
		m_pLib->CloseLibrary();
dvander@298
    46
		m_pLib = NULL;
dvander@298
    47
		snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
dvander@298
    48
		return;
dvander@298
    49
	}
dvander@298
    50
dvander@298
    51
	if (m_pAPI->IsMetamodExtension())
dvander@298
    52
	{
dvander@304
    53
		bool already;
dvander@304
    54
		m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max);
dvander@298
    55
	}
dvander@298
    56
dvander@298
    57
	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
dvander@298
    58
dvander@298
    59
	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, g_SourceMod.IsLateLoadInMap()))
dvander@298
    60
	{
dvander@298
    61
		if (m_pAPI->IsMetamodExtension())
dvander@298
    62
		{
dvander@304
    63
			if (m_PlId)
dvander@304
    64
			{
dvander@304
    65
				char dummy[255];
dvander@304
    66
				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
dvander@304
    67
				m_PlId = 0;
dvander@304
    68
			}
dvander@298
    69
		}
dvander@298
    70
		m_pAPI = NULL;
dvander@298
    71
		m_pLib->CloseLibrary();
dvander@298
    72
		m_pLib = NULL;
dvander@298
    73
		g_ShareSys.DestroyIdentity(m_pIdentToken);
dvander@298
    74
		m_pIdentToken = NULL;
dvander@298
    75
		return;
dvander@298
    76
	}
dvander@298
    77
}
dvander@298
    78
dvander@298
    79
CExtension::~CExtension()
dvander@298
    80
{
dvander@298
    81
	if (m_pAPI)
dvander@298
    82
	{
dvander@298
    83
		m_pAPI->OnExtensionUnload();
dvander@304
    84
		if (m_PlId)
dvander@304
    85
		{
dvander@304
    86
			g_pMMPlugins->Unload(m_PlId, true, NULL, 0);
dvander@304
    87
		}
dvander@298
    88
	}
dvander@298
    89
dvander@298
    90
	if (m_pIdentToken)
dvander@298
    91
	{
dvander@298
    92
		g_ShareSys.DestroyIdentity(m_pIdentToken);
dvander@298
    93
	}
dvander@298
    94
dvander@298
    95
	if (m_pLib)
dvander@298
    96
	{
dvander@298
    97
		m_pLib->CloseLibrary();
dvander@298
    98
	}
dvander@298
    99
}
dvander@298
   100
dvander@305
   101
void CExtension::AddPlugin(IPlugin *pPlugin)
dvander@305
   102
{
dvander@305
   103
	m_Plugins.push_back(pPlugin);
dvander@305
   104
}
dvander@305
   105
dvander@305
   106
void CExtension::RemovePlugin(IPlugin *pPlugin)
dvander@305
   107
{
dvander@305
   108
	m_Plugins.remove(pPlugin);
dvander@305
   109
}
dvander@305
   110
dvander@298
   111
void CExtension::SetError(const char *error)
dvander@298
   112
{
dvander@298
   113
	m_Error.assign(error);
dvander@298
   114
}
dvander@298
   115
dvander@298
   116
IExtensionInterface *CExtension::GetAPI()
dvander@298
   117
{
dvander@298
   118
	return m_pAPI;
dvander@298
   119
}
dvander@298
   120
dvander@298
   121
const char *CExtension::GetFilename()
dvander@298
   122
{
dvander@298
   123
	return m_File.c_str();
dvander@298
   124
}
dvander@298
   125
dvander@298
   126
IdentityToken_t *CExtension::GetIdentity()
dvander@298
   127
{
dvander@298
   128
	return m_pIdentToken;
dvander@298
   129
}
dvander@298
   130
dvander@298
   131
bool CExtension::IsLoaded()
dvander@298
   132
{
dvander@298
   133
	return (m_pLib != NULL);
dvander@298
   134
}
dvander@298
   135
dvander@304
   136
void CExtension::AddDependency(IfaceInfo *pInfo)
dvander@304
   137
{
dvander@304
   138
	m_Deps.push_back(*pInfo);
dvander@304
   139
}
dvander@304
   140
dvander@304
   141
ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
dvander@304
   142
{
dvander@304
   143
	List<IfaceInfo>::iterator iter = m_Deps.begin();
dvander@304
   144
dvander@304
   145
	if (iter == m_Deps.end())
dvander@304
   146
	{
dvander@304
   147
		return NULL;
dvander@304
   148
	}
dvander@304
   149
dvander@304
   150
	if (pOwner)
dvander@304
   151
	{
dvander@304
   152
		*pOwner = (*iter).owner;
dvander@304
   153
	}
dvander@304
   154
	if (pInterface)
dvander@304
   155
	{
dvander@304
   156
		*pInterface = (*iter).iface;
dvander@304
   157
	}
dvander@304
   158
dvander@304
   159
	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
dvander@304
   160
dvander@304
   161
	return (ITERATOR *)pIter;
dvander@304
   162
}
dvander@304
   163
dvander@304
   164
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
dvander@304
   165
{
dvander@304
   166
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
dvander@304
   167
	List<IfaceInfo>::iterator _iter;
dvander@304
   168
dvander@304
   169
	if (_iter == m_Deps.end())
dvander@304
   170
	{
dvander@304
   171
		return false;
dvander@304
   172
	}
dvander@304
   173
dvander@304
   174
	_iter++;
dvander@304
   175
dvander@304
   176
	if (pOwner)
dvander@304
   177
	{
dvander@304
   178
		*pOwner = (*_iter).owner;
dvander@304
   179
	}
dvander@304
   180
	if (pInterface)
dvander@304
   181
	{
dvander@304
   182
		*pInterface = (*_iter).iface;
dvander@304
   183
	}
dvander@304
   184
dvander@304
   185
	*pIter = _iter;
dvander@304
   186
dvander@304
   187
	if (_iter == m_Deps.end())
dvander@304
   188
	{
dvander@304
   189
		return false;
dvander@304
   190
	}
dvander@304
   191
dvander@304
   192
	return true;
dvander@304
   193
}
dvander@304
   194
dvander@304
   195
void CExtension::FreeDependencyIterator(ITERATOR *iter)
dvander@304
   196
{
dvander@304
   197
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
dvander@304
   198
dvander@304
   199
	delete pIter;
dvander@304
   200
}
dvander@304
   201
dvander@304
   202
void CExtension::AddInterface(SMInterface *pInterface)
dvander@304
   203
{
dvander@304
   204
	m_Interfaces.push_back(pInterface);
dvander@304
   205
}
dvander@304
   206
dvander@304
   207
/*********************
dvander@304
   208
 * EXTENSION MANAGER *
dvander@304
   209
 *********************/
dvander@304
   210
dvander@298
   211
void CExtensionManager::OnSourceModAllInitialized()
dvander@298
   212
{
dvander@298
   213
	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
dvander@305
   214
	g_PluginSys.AddPluginsListener(this);
dvander@310
   215
	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
dvander@298
   216
}
dvander@298
   217
dvander@298
   218
void CExtensionManager::OnSourceModShutdown()
dvander@298
   219
{
dvander@310
   220
	g_RootMenu.RemoveRootConsoleCommand("exts", this);
dvander@305
   221
	g_PluginSys.RemovePluginsListener(this);
dvander@298
   222
	g_ShareSys.DestroyIdentType(g_ExtType);
dvander@298
   223
}
dvander@298
   224
dvander@298
   225
IExtension *CExtensionManager::LoadAutoExtension(const char *path)
dvander@298
   226
{
dvander@304
   227
	IExtension *pAlready;
dvander@304
   228
	if ((pAlready=FindExtensionByFile(path)) != NULL)
dvander@304
   229
	{
dvander@304
   230
		return pAlready;
dvander@304
   231
	}
dvander@304
   232
dvander@298
   233
	char error[256];
dvander@298
   234
	CExtension *p = new CExtension(path, error, sizeof(error));
dvander@298
   235
dvander@298
   236
	if (!p->IsLoaded())
dvander@298
   237
	{
dvander@305
   238
		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
dvander@298
   239
		p->SetError(error);
dvander@298
   240
	}
dvander@298
   241
dvander@298
   242
	m_Libs.push_back(p);
dvander@298
   243
dvander@298
   244
	return p;
dvander@298
   245
}
dvander@298
   246
dvander@298
   247
IExtension *CExtensionManager::FindExtensionByFile(const char *file)
dvander@298
   248
{
dvander@298
   249
	List<CExtension *>::iterator iter;
dvander@298
   250
	CExtension *pExt;
dvander@298
   251
dvander@298
   252
	/* Make sure the file direction is right */
dvander@298
   253
	char path[PLATFORM_MAX_PATH+1];
dvander@298
   254
	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
dvander@298
   255
dvander@298
   256
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@298
   257
	{
dvander@298
   258
		pExt = (*iter);
dvander@298
   259
		if (strcmp(pExt->GetFilename(), path) == 0)
dvander@298
   260
		{
dvander@298
   261
			return pExt;
dvander@298
   262
		}
dvander@298
   263
	}
dvander@298
   264
dvander@298
   265
	return NULL;
dvander@298
   266
}
dvander@298
   267
dvander@298
   268
IExtension *CExtensionManager::FindExtensionByName(const char *ext)
dvander@298
   269
{
dvander@298
   270
	List<CExtension *>::iterator iter;
dvander@298
   271
	CExtension *pExt;
dvander@298
   272
	IExtensionInterface *pAPI;
dvander@298
   273
	const char *name;
dvander@298
   274
dvander@298
   275
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@298
   276
	{
dvander@298
   277
		pExt = (*iter);
dvander@298
   278
		if (!pExt->IsLoaded())
dvander@298
   279
		{
dvander@298
   280
			continue;
dvander@298
   281
		}
dvander@298
   282
		if ((pAPI = pExt->GetAPI()) == NULL)
dvander@298
   283
		{
dvander@298
   284
			continue;
dvander@298
   285
		}
dvander@298
   286
		name = pAPI->GetExtensionName();
dvander@298
   287
		if (!name)
dvander@298
   288
		{
dvander@298
   289
			continue;
dvander@298
   290
		}
dvander@298
   291
		if (strcmp(name, ext) == 0)
dvander@298
   292
		{
dvander@298
   293
			return pExt;
dvander@298
   294
		}
dvander@298
   295
	}
dvander@298
   296
dvander@298
   297
	return NULL;
dvander@298
   298
}
dvander@298
   299
dvander@298
   300
IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max)
dvander@298
   301
{
dvander@304
   302
	IExtension *pAlready;
dvander@304
   303
	if ((pAlready=FindExtensionByFile(file)) != NULL)
dvander@304
   304
	{
dvander@304
   305
		return pAlready;
dvander@304
   306
	}
dvander@304
   307
dvander@298
   308
	CExtension *pExt = new CExtension(file, error, err_max);
dvander@298
   309
dvander@298
   310
	/* :NOTE: lifetime is currently ignored */
dvander@298
   311
dvander@298
   312
	if (!pExt->IsLoaded())
dvander@298
   313
	{
dvander@298
   314
		delete pExt;
dvander@298
   315
		return NULL;
dvander@298
   316
	}
dvander@298
   317
dvander@298
   318
	m_Libs.push_back(pExt);
dvander@298
   319
dvander@298
   320
	return pExt;
dvander@298
   321
}
dvander@298
   322
dvander@304
   323
void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo)
dvander@298
   324
{
dvander@304
   325
	CExtension *pExt = (CExtension *)pOwner;
dvander@304
   326
dvander@304
   327
	pExt->AddDependency(pInfo);
dvander@304
   328
}
dvander@304
   329
dvander@304
   330
void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
dvander@304
   331
{
dvander@304
   332
	CExtension *pExt = (CExtension *)pOwner;
dvander@304
   333
dvander@304
   334
	pExt->AddInterface(pInterface);
dvander@298
   335
}
dvander@298
   336
dvander@305
   337
void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
dvander@305
   338
{
dvander@305
   339
	CExtension *pExt = (CExtension *)pParent;
dvander@305
   340
dvander@305
   341
	pExt->AddPlugin(pPlugin);
dvander@305
   342
}
dvander@305
   343
dvander@305
   344
void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
dvander@305
   345
{
dvander@305
   346
	List<CExtension *>::iterator iter;
dvander@305
   347
dvander@305
   348
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@305
   349
	{
dvander@305
   350
		(*iter)->RemovePlugin(plugin);
dvander@305
   351
	}
dvander@305
   352
}
dvander@305
   353
dvander@311
   354
CExtension *CExtensionManager::FindByOrder(unsigned int num)
dvander@311
   355
{
dvander@311
   356
	if (num < 1 || num > m_Libs.size())
dvander@311
   357
	{
dvander@311
   358
		return NULL;
dvander@311
   359
	}
dvander@311
   360
dvander@311
   361
	List<CExtension *>::iterator iter = m_Libs.begin();
dvander@311
   362
dvander@311
   363
	while (iter != m_Libs.end())
dvander@311
   364
	{
dvander@311
   365
		if (--num == 0)
dvander@311
   366
		{
dvander@311
   367
			return (*iter);
dvander@311
   368
		}
dvander@311
   369
		iter++;
dvander@311
   370
	}
dvander@311
   371
dvander@311
   372
	return NULL;
dvander@311
   373
}
dvander@311
   374
dvander@304
   375
bool CExtensionManager::UnloadExtension(IExtension *_pExt)
dvander@298
   376
{
dvander@304
   377
	if (!_pExt)
dvander@304
   378
	{
dvander@304
   379
		return false;
dvander@304
   380
	}
dvander@304
   381
dvander@304
   382
	CExtension *pExt = (CExtension *)_pExt;
dvander@304
   383
dvander@304
   384
	if (m_Libs.find(pExt) == m_Libs.end())
dvander@304
   385
	{
dvander@304
   386
		return false;
dvander@304
   387
	}
dvander@304
   388
dvander@304
   389
	/* First remove us from internal lists */
dvander@304
   390
	g_ShareSys.RemoveInterfaces(_pExt);
dvander@304
   391
	m_Libs.remove(pExt);
dvander@304
   392
dvander@304
   393
	List<CExtension *> UnloadQueue;
dvander@298
   394
dvander@304
   395
	/* Handle dependencies */
dvander@304
   396
	if (pExt->IsLoaded())
dvander@304
   397
	{
dvander@305
   398
		/* Unload any dependent plugins */
dvander@305
   399
		List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
dvander@305
   400
		while (p_iter != pExt->m_Plugins.end())
dvander@305
   401
		{
dvander@311
   402
			/* We have to manually unlink ourselves here, since we're no longer being managed */
dvander@305
   403
			g_PluginSys.UnloadPlugin((*p_iter));
dvander@311
   404
			p_iter = pExt->m_Plugins.erase(p_iter);
dvander@305
   405
		}
dvander@305
   406
dvander@304
   407
		/* Notify and/or unload all dependencies */
dvander@304
   408
		List<CExtension *>::iterator c_iter;
dvander@304
   409
		CExtension *pDep;
dvander@304
   410
		IExtensionInterface *pAPI;
dvander@304
   411
		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
dvander@304
   412
		{
dvander@304
   413
			pDep = (*c_iter);
dvander@304
   414
			if ((pAPI=pDep->GetAPI()) == NULL)
dvander@304
   415
			{
dvander@304
   416
				continue;
dvander@304
   417
			}
dvander@311
   418
			if (pDep == pExt)
dvander@311
   419
			{
dvander@311
   420
				continue;
dvander@311
   421
			}
dvander@304
   422
			/* Now, get its dependency list */
dvander@304
   423
			bool dropped = false;
dvander@304
   424
			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
dvander@304
   425
			while (i_iter != pDep->m_Deps.end())
dvander@304
   426
			{
dvander@304
   427
				if ((*i_iter).owner == _pExt)
dvander@304
   428
				{
dvander@304
   429
					if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface))
dvander@304
   430
					{
dvander@304
   431
						dropped = true;
dvander@304
   432
					}
dvander@304
   433
					pAPI->NotifyInterfaceDrop((*i_iter).iface);
dvander@304
   434
					i_iter = pDep->m_Deps.erase(i_iter);
dvander@304
   435
				} else {
dvander@304
   436
					i_iter++;
dvander@304
   437
				}
dvander@304
   438
			}
dvander@304
   439
		}
dvander@304
   440
	}
dvander@304
   441
dvander@304
   442
	List<CExtension *>::iterator iter;
dvander@304
   443
	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
dvander@304
   444
	{
dvander@304
   445
		/* NOTE: This is safe because the unload function backs out of anything not present */
dvander@304
   446
		UnloadExtension((*iter));
dvander@304
   447
	}
dvander@304
   448
dvander@298
   449
	return true;
dvander@298
   450
}
dvander@310
   451
dvander@310
   452
void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argcount)
dvander@310
   453
{
dvander@310
   454
	if (argcount >= 3)
dvander@310
   455
	{
dvander@310
   456
		const char *cmd = g_RootMenu.GetArgument(2);
dvander@310
   457
		if (strcmp(cmd, "list") == 0)
dvander@310
   458
		{
dvander@310
   459
			List<CExtension *>::iterator iter;
dvander@310
   460
			CExtension *pExt;
dvander@310
   461
			unsigned int num = 1;
dvander@310
   462
			switch (m_Libs.size())
dvander@310
   463
			{
dvander@310
   464
			case 1:
dvander@310
   465
				{
dvander@310
   466
					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
dvander@310
   467
					break;
dvander@310
   468
				}
dvander@310
   469
			case 0:
dvander@310
   470
				{
dvander@310
   471
					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
dvander@310
   472
					break;
dvander@310
   473
				}
dvander@310
   474
			default:
dvander@310
   475
				{
dvander@310
   476
					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
dvander@310
   477
					break;
dvander@310
   478
				}
dvander@310
   479
			}
dvander@310
   480
			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
dvander@310
   481
			{
dvander@310
   482
				pExt = (*iter);
dvander@310
   483
				if (pExt->IsLoaded())
dvander@310
   484
				{
dvander@310
   485
					IExtensionInterface *pAPI = pExt->GetAPI();
dvander@310
   486
					const char *name = pAPI->GetExtensionName();
dvander@310
   487
					const char *version = pAPI->GetExtensionVerString();
dvander@310
   488
					const char *descr = pAPI->GetExtensionDescription();
dvander@310
   489
					g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
dvander@310
   490
				} else {
dvander@310
   491
					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
dvander@310
   492
				}
dvander@310
   493
			}
dvander@310
   494
			return;
dvander@310
   495
		} else if (strcmp(cmd, "info") == 0) {
dvander@310
   496
			if (argcount < 4)
dvander@310
   497
			{
dvander@310
   498
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
dvander@310
   499
				return;
dvander@310
   500
			}
dvander@310
   501
dvander@310
   502
			const char *sId = g_RootMenu.GetArgument(3);
dvander@310
   503
			unsigned int id = atoi(sId);
dvander@310
   504
			if (id <= 0)
dvander@310
   505
			{
dvander@310
   506
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
dvander@310
   507
				return;
dvander@310
   508
			}
dvander@310
   509
dvander@310
   510
			if (m_Libs.size() == 0)
dvander@310
   511
			{
dvander@310
   512
				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
dvander@310
   513
				return;
dvander@310
   514
			}
dvander@310
   515
dvander@310
   516
			if (id > m_Libs.size())
dvander@310
   517
			{
dvander@310
   518
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
dvander@310
   519
				return;
dvander@310
   520
			}
dvander@310
   521
dvander@310
   522
			List<CExtension *>::iterator iter = m_Libs.begin();
dvander@310
   523
			CExtension *pExt = NULL;
dvander@310
   524
			while (iter != m_Libs.end())
dvander@310
   525
			{
dvander@310
   526
				if (--id == 0)
dvander@310
   527
				{
dvander@310
   528
					pExt = (*iter);
dvander@310
   529
					break;
dvander@310
   530
				}
dvander@310
   531
				iter++;
dvander@310
   532
			}
dvander@310
   533
			/* This should never happen */
dvander@310
   534
			if (!pExt)
dvander@310
   535
			{
dvander@310
   536
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
dvander@310
   537
				return;
dvander@310
   538
			}
dvander@310
   539
dvander@310
   540
			if (!pExt->IsLoaded())
dvander@310
   541
			{
dvander@310
   542
				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
dvander@310
   543
				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
dvander@310
   544
			} else {
dvander@310
   545
				IExtensionInterface *pAPI = pExt->GetAPI();
dvander@310
   546
				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
dvander@310
   547
				g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
dvander@310
   548
				g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
dvander@310
   549
				g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
dvander@310
   550
				g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
dvander@310
   551
				g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no");
dvander@310
   552
			}
dvander@310
   553
			return;
dvander@311
   554
		} else if (strcmp(cmd, "unload") == 0) {
dvander@311
   555
			if (argcount < 4)
dvander@311
   556
			{
dvander@311
   557
				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
dvander@311
   558
				return;
dvander@311
   559
			}
dvander@311
   560
dvander@311
   561
			const char *arg = g_RootMenu.GetArgument(3);
dvander@311
   562
			unsigned int num = atoi(arg);
dvander@311
   563
			CExtension *pExt = FindByOrder(num);
dvander@311
   564
dvander@311
   565
			if (!pExt)
dvander@311
   566
			{
dvander@311
   567
				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
dvander@311
   568
				return;
dvander@311
   569
			}
dvander@311
   570
dvander@311
   571
			if (argcount > 4 && pExt->unload_code)
dvander@311
   572
			{
dvander@311
   573
				const char *unload = g_RootMenu.GetArgument(4);
dvander@311
   574
				if (pExt->unload_code == atoi(unload))
dvander@311
   575
				{
dvander@311
   576
					char filename[PLATFORM_MAX_PATH+1];
dvander@311
   577
					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
dvander@311
   578
					UnloadExtension(pExt);
dvander@311
   579
					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
dvander@311
   580
				} else {
dvander@311
   581
					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
dvander@311
   582
				}
dvander@311
   583
				return;
dvander@311
   584
			}
dvander@311
   585
dvander@311
   586
			if (!pExt->IsLoaded() 
dvander@311
   587
				|| (!pExt->m_Deps.size() && !pExt->m_Plugins.size()))
dvander@311
   588
			{
dvander@311
   589
				char filename[PLATFORM_MAX_PATH+1];
dvander@311
   590
				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
dvander@311
   591
				UnloadExtension(pExt);
dvander@311
   592
				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
dvander@311
   593
				return;
dvander@311
   594
			} else {
dvander@311
   595
				List<IPlugin *> plugins;
dvander@311
   596
				if (pExt->m_Deps.size())
dvander@311
   597
				{
dvander@311
   598
					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
dvander@311
   599
					List<CExtension *>::iterator iter;
dvander@311
   600
					CExtension *pOther;
dvander@311
   601
					/* Get list of all extensions */
dvander@311
   602
					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
dvander@311
   603
					{
dvander@311
   604
						List<IfaceInfo>::iterator i_iter;
dvander@311
   605
						pOther = (*iter);
dvander@311
   606
						if (!pOther->IsLoaded() || pOther == pExt)
dvander@311
   607
						{
dvander@311
   608
							continue;
dvander@311
   609
						}
dvander@311
   610
						/* Get their dependencies */
dvander@311
   611
						for (i_iter=pOther->m_Deps.begin();
dvander@311
   612
							 i_iter!=pOther->m_Deps.end();
dvander@311
   613
							 i_iter++)
dvander@311
   614
						{
dvander@311
   615
							/* Is this dependency to us? */
dvander@311
   616
							if ((*i_iter).owner != pExt)
dvander@311
   617
							{
dvander@311
   618
								continue;
dvander@311
   619
							}
dvander@311
   620
							/* Will our dependent care? */
dvander@311
   621
							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
dvander@311
   622
							{
dvander@311
   623
								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
dvander@311
   624
								/* Add to plugin unload list */
dvander@311
   625
								List<IPlugin *>::iterator p_iter;
dvander@311
   626
								for (p_iter=pOther->m_Plugins.begin();
dvander@311
   627
									 p_iter!=pOther->m_Plugins.end();
dvander@311
   628
									 p_iter++)
dvander@311
   629
								{
dvander@311
   630
									if (plugins.find((*p_iter)) == plugins.end())
dvander@311
   631
									{
dvander@311
   632
										plugins.push_back((*p_iter));
dvander@311
   633
									}
dvander@311
   634
								}
dvander@311
   635
							}
dvander@311
   636
						}
dvander@311
   637
					}
dvander@311
   638
				}
dvander@311
   639
				if (pExt->m_Plugins.size())
dvander@311
   640
				{
dvander@311
   641
					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
dvander@311
   642
					List<IPlugin *>::iterator iter;
dvander@311
   643
					IPlugin *pPlugin;
dvander@311
   644
					for (iter = pExt->m_Plugins.begin(); iter != pExt->m_Plugins.end(); iter++)
dvander@311
   645
					{
dvander@311
   646
						pPlugin = (*iter);
dvander@311
   647
						if (plugins.find(pPlugin) == plugins.end())
dvander@311
   648
						{
dvander@311
   649
							plugins.push_back(pPlugin);
dvander@311
   650
						}
dvander@311
   651
					}
dvander@311
   652
					for (iter = plugins.begin(); iter != plugins.end(); iter++)
dvander@311
   653
					{
dvander@311
   654
						pPlugin = (*iter);
dvander@311
   655
						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
dvander@311
   656
					}
dvander@311
   657
				}
dvander@311
   658
				srand(static_cast<int>(time(NULL)));
dvander@311
   659
				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
dvander@311
   660
				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
dvander@311
   661
				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
dvander@311
   662
dvander@311
   663
				return;
dvander@311
   664
			}
dvander@310
   665
		}
dvander@310
   666
	}
dvander@310
   667
dvander@310
   668
	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
dvander@310
   669
	g_RootMenu.DrawGenericOption("info", "Extra extension information");
dvander@310
   670
	g_RootMenu.DrawGenericOption("list", "List extensions");
dvander@311
   671
	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
dvander@310
   672
}