core/systems/ExtensionSys.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 1993 8c08aaff4d39
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdlib.h>
    33 #include "ExtensionSys.h"
    34 #include "LibrarySys.h"
    35 #include "ShareSys.h"
    36 #include "Logger.h"
    37 #include "sourcemm_api.h"
    38 #include "sm_srvcmds.h"
    39 #include "sm_stringutil.h"
    40 
    41 CExtensionManager g_Extensions;
    42 IdentityType_t g_ExtType;
    43 
    44 void CExtension::Initialize(const char *filename, const char *path)
    45 {
    46 	m_pAPI = NULL;
    47 	m_pIdentToken = NULL;
    48 	unload_code = 0;
    49 	m_bFullyLoaded = false;
    50 	m_File.assign(filename);
    51 	m_Path.assign(path);
    52 }
    53 
    54 CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path)
    55 {
    56 	Initialize(filename, path);
    57 	m_pAPI = pAPI;
    58 }
    59 
    60 CLocalExtension::CLocalExtension(const char *filename)
    61 {
    62 	m_PlId = 0;
    63 	m_pLib = NULL;
    64 
    65 	char path[PLATFORM_MAX_PATH];
    66 
    67 	/* First see if there is an engine specific build! */
    68 	g_SourceMod.BuildPath(Path_SM, 
    69 		path, 
    70 		PLATFORM_MAX_PATH, 
    71 #if defined METAMOD_PLAPI_VERSION
    72 #if defined ORANGEBOX_BUILD
    73 		"extensions/auto.2.ep2/%s",
    74 #else
    75 		"extensions/auto.2.ep1/%s",
    76 #endif //ORANGEBOX_BUILD
    77 #else  //METAMOD_PLAPI_VERSION
    78 		"extensions/auto.1.ep1/%s",
    79 #endif //METAMOD_PLAPI_VERSION
    80 		filename);
    81 
    82 	/* Try the "normal" version */
    83 	if (!g_LibSys.IsPathFile(path))
    84 	{
    85 		g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
    86 	}
    87 
    88 	Initialize(filename, path);
    89 }
    90 
    91 bool CRemoteExtension::Load(char *error, size_t maxlength)
    92 {
    93 	if (!PerformAPICheck(error, maxlength))
    94 	{
    95 		m_pAPI = NULL;
    96 		return false;
    97 	}
    98 
    99 	if (!CExtension::Load(error, maxlength))
   100 	{
   101 		m_pAPI = NULL;
   102 		return false;
   103 	}
   104 
   105 	return true;
   106 }
   107 
   108 bool CLocalExtension::Load(char *error, size_t maxlength)
   109 {
   110 	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
   111 
   112 	if (m_pLib == NULL)
   113 	{
   114 		return false;
   115 	}
   116 
   117 	typedef IExtensionInterface *(*GETAPI)();
   118 	GETAPI pfnGetAPI = NULL;
   119 
   120 	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
   121 	{
   122 		m_pLib->CloseLibrary();
   123 		m_pLib = NULL;
   124 		snprintf(error, maxlength, "Unable to find extension entry point");
   125 		return false;
   126 	}
   127 
   128 	m_pAPI = pfnGetAPI();
   129 
   130 	/* Check pointer and version */
   131 	if (!PerformAPICheck(error, maxlength))
   132 	{
   133 		m_pLib->CloseLibrary();
   134 		m_pLib = NULL;
   135 		m_pAPI = NULL;
   136 		return false;
   137 	}
   138 
   139 	/* Load as MM:S */
   140 	if (m_pAPI->IsMetamodExtension())
   141 	{
   142 		bool already;
   143 		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
   144 	}
   145 
   146 	if (!CExtension::Load(error, maxlength))
   147 	{
   148 		if (m_pAPI->IsMetamodExtension())
   149 		{
   150 			if (m_PlId)
   151 			{
   152 				char dummy[255];
   153 				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
   154 				m_PlId = 0;
   155 			}
   156 		}
   157 		m_pLib->CloseLibrary();
   158 		m_pLib = NULL;
   159 		m_pAPI = NULL;
   160 		return false;
   161 	}
   162 
   163 	return true;
   164 }
   165 
   166 void CRemoteExtension::Unload()
   167 {
   168 }
   169 
   170 void CLocalExtension::Unload()
   171 {
   172 	if (m_pAPI != NULL && m_PlId)
   173 	{
   174 		char temp_buffer[255];
   175 		g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
   176 		m_PlId = 0;
   177 	}
   178 
   179 	if (m_pLib != NULL)
   180 	{
   181 		m_pLib->CloseLibrary();
   182 		m_pLib = NULL;
   183 	}
   184 }
   185 
   186 bool CRemoteExtension::IsExternal()
   187 {
   188 	return true;
   189 }
   190 
   191 bool CLocalExtension::IsExternal()
   192 {
   193 	return false;
   194 }
   195 
   196 CExtension::~CExtension()
   197 {
   198 	DestroyIdentity();
   199 }
   200 
   201 bool CExtension::PerformAPICheck(char *error, size_t maxlength)
   202 {
   203 	if (!m_pAPI)
   204 	{
   205 		snprintf(error, maxlength, "No IExtensionInterface instance provided");
   206 		return false;
   207 	}
   208 	
   209 	if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
   210 	{
   211 		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
   212 		return false;
   213 	}
   214 
   215 	return true;
   216 }
   217 
   218 bool CExtension::Load(char *error, size_t maxlength)
   219 {
   220 	CreateIdentity();
   221 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
   222 	{
   223 		DestroyIdentity();
   224 		return false;
   225 	}
   226 	else
   227 	{
   228 		/* Check if we're past load time */
   229 		if (!g_SourceMod.IsMapLoading())
   230 		{
   231 			m_pAPI->OnExtensionsAllLoaded();
   232 		}
   233 	}
   234 
   235 	return true;
   236 }
   237 
   238 void CExtension::CreateIdentity()
   239 {
   240 	if (m_pIdentToken != NULL)
   241 	{
   242 		return;
   243 	}
   244 
   245 	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
   246 }
   247 
   248 void CExtension::DestroyIdentity()
   249 {
   250 	if (m_pIdentToken == NULL)
   251 	{
   252 		return;
   253 	}
   254 
   255 	g_ShareSys.DestroyIdentity(m_pIdentToken);
   256 	m_pIdentToken = NULL;
   257 }
   258 
   259 void CExtension::MarkAllLoaded()
   260 {
   261 	assert(m_bFullyLoaded == false);
   262 	if (!m_bFullyLoaded)
   263 	{
   264 		m_bFullyLoaded = true;
   265 		m_pAPI->OnExtensionsAllLoaded();
   266 	}
   267 }
   268 
   269 void CExtension::AddPlugin(IPlugin *pPlugin)
   270 {
   271 	/* Unfortunately we have to do this :( */
   272 	if (m_Plugins.find(pPlugin) != m_Plugins.end())
   273 	{
   274 		m_Plugins.push_back(pPlugin);
   275 	}
   276 }
   277 
   278 void CExtension::RemovePlugin(IPlugin *pPlugin)
   279 {
   280 	m_Plugins.remove(pPlugin);
   281 
   282 	List<WeakNative>::iterator iter = m_WeakNatives.begin();
   283 	while (iter != m_WeakNatives.end())
   284 	{
   285 		if ((*iter).pl == pPlugin)
   286 		{
   287 			iter = m_WeakNatives.erase(iter);
   288 		}
   289 		else
   290 		{
   291 			iter++;
   292 		}
   293 	}
   294 
   295 	iter = m_ReplacedNatives.begin();
   296 	while (iter != m_ReplacedNatives.end())
   297 	{
   298 		if ((*iter).pl == pPlugin)
   299 		{
   300 			iter = m_ReplacedNatives.erase(iter);
   301 		}
   302 		else
   303 		{
   304 			iter++;
   305 		}
   306 	}
   307 }
   308 
   309 void CExtension::SetError(const char *error)
   310 {
   311 	m_Error.assign(error);
   312 }
   313 
   314 IExtensionInterface *CExtension::GetAPI()
   315 {
   316 	return m_pAPI;
   317 }
   318 
   319 const char *CExtension::GetFilename()
   320 {
   321 	return m_File.c_str();
   322 }
   323 
   324 IdentityToken_t *CExtension::GetIdentity()
   325 {
   326 	return m_pIdentToken;
   327 }
   328 
   329 bool CRemoteExtension::IsLoaded()
   330 {
   331 	return (m_pAPI != NULL);
   332 }
   333 
   334 bool CLocalExtension::IsLoaded()
   335 {
   336 	return (m_pLib != NULL);
   337 }
   338 
   339 void CExtension::AddDependency(IfaceInfo *pInfo)
   340 {
   341 	if (m_Deps.find(*pInfo) == m_Deps.end())
   342 	{
   343 		m_Deps.push_back(*pInfo);
   344 	}
   345 }
   346 
   347 bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2)
   348 {
   349 	return (i1.iface == i2.iface) && (i1.owner == i2.owner);
   350 }
   351 
   352 void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
   353 {
   354 	IfaceInfo info;
   355 	info.iface = iface;
   356 	info.owner = pOther;
   357 
   358 	List<IfaceInfo>::iterator iter;
   359 	for (iter = m_ChildDeps.begin();
   360 		 iter != m_ChildDeps.end();
   361 		 iter++)
   362 	{
   363 		IfaceInfo &other = (*iter);
   364 		if (other == info)
   365 		{
   366 			return;
   367 		}
   368 	}
   369 
   370 	m_ChildDeps.push_back(info);
   371 }
   372 
   373 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
   374 {
   375 	List<IfaceInfo>::iterator iter = m_Deps.begin();
   376 
   377 	if (iter == m_Deps.end())
   378 	{
   379 		return NULL;
   380 	}
   381 
   382 	if (pOwner)
   383 	{
   384 		*pOwner = (*iter).owner;
   385 	}
   386 	if (pInterface)
   387 	{
   388 		*pInterface = (*iter).iface;
   389 	}
   390 
   391 	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
   392 
   393 	return (ITERATOR *)pIter;
   394 }
   395 
   396 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
   397 {
   398 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   399 	List<IfaceInfo>::iterator _iter;
   400 
   401 	if (_iter == m_Deps.end())
   402 	{
   403 		return false;
   404 	}
   405 
   406 	_iter++;
   407 
   408 	if (pOwner)
   409 	{
   410 		*pOwner = (*_iter).owner;
   411 	}
   412 	if (pInterface)
   413 	{
   414 		*pInterface = (*_iter).iface;
   415 	}
   416 
   417 	*pIter = _iter;
   418 
   419 	if (_iter == m_Deps.end())
   420 	{
   421 		return false;
   422 	}
   423 
   424 	return true;
   425 }
   426 
   427 void CExtension::FreeDependencyIterator(ITERATOR *iter)
   428 {
   429 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   430 
   431 	delete pIter;
   432 }
   433 
   434 void CExtension::AddInterface(SMInterface *pInterface)
   435 {
   436 	m_Interfaces.push_back(pInterface);
   437 }
   438 
   439 bool CExtension::IsRunning(char *error, size_t maxlength)
   440 {
   441 	if (!IsLoaded())
   442 	{
   443 		if (error)
   444 		{
   445 			snprintf(error, maxlength, "%s", m_Error.c_str());
   446 		}
   447 		return false;
   448 	}
   449 
   450 	return m_pAPI->QueryRunning(error, maxlength);
   451 }
   452 
   453 void CExtension::AddLibrary(const char *library)
   454 {
   455 	m_Libraries.push_back(library);
   456 }
   457 
   458 /*********************
   459  * EXTENSION MANAGER *
   460  *********************/
   461 
   462 void CExtensionManager::OnSourceModAllInitialized()
   463 {
   464 	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
   465 	g_PluginSys.AddPluginsListener(this);
   466 	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
   467 	g_ShareSys.AddInterface(NULL, this);
   468 }
   469 
   470 void CExtensionManager::OnSourceModShutdown()
   471 {
   472 	g_RootMenu.RemoveRootConsoleCommand("exts", this);
   473 	g_ShareSys.DestroyIdentType(g_ExtType);
   474 }
   475 
   476 void CExtensionManager::Shutdown()
   477 {
   478 	List<CExtension *>::iterator iter;
   479 
   480 	while ((iter = m_Libs.begin()) != m_Libs.end())
   481 	{
   482 		UnloadExtension((*iter));
   483 	}
   484 }
   485 
   486 void CExtensionManager::TryAutoload()
   487 {
   488 	char path[PLATFORM_MAX_PATH];
   489 
   490 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
   491 
   492 	IDirectory *pDir = g_LibSys.OpenDirectory(path);
   493 	if (!pDir)
   494 	{
   495 		return;
   496 	}
   497 
   498 	const char *lfile;
   499 	size_t len;
   500 	while (pDir->MoreFiles())
   501 	{
   502 		if (pDir->IsEntryDirectory())
   503 		{
   504 			pDir->NextEntry();
   505 			continue;
   506 		}
   507 
   508 		lfile = pDir->GetEntryName();
   509 		len = strlen(lfile);
   510 		if (len <= 9) /* size of ".autoload" */
   511 		{
   512 			pDir->NextEntry();
   513 			continue;
   514 		}
   515 
   516 		if (strcmp(&lfile[len - 9], ".autoload") != 0)
   517 		{
   518 			pDir->NextEntry();
   519 			continue;
   520 		}
   521 
   522 		char file[PLATFORM_MAX_PATH];
   523 		len = UTIL_Format(file, sizeof(file), "%s", lfile);
   524 		strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT);
   525 
   526 		LoadAutoExtension(file);
   527 		
   528 		pDir->NextEntry();
   529 	}
   530 }
   531 
   532 IExtension *CExtensionManager::LoadAutoExtension(const char *path)
   533 {
   534 	if (!strstr(path, "." PLATFORM_LIB_EXT))
   535 	{
   536 		char newpath[PLATFORM_MAX_PATH];
   537 		g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT);
   538 		return LoadAutoExtension(newpath);
   539 	}
   540 
   541 	IExtension *pAlready;
   542 	if ((pAlready=FindExtensionByFile(path)) != NULL)
   543 	{
   544 		return pAlready;
   545 	}
   546 
   547 	char error[256];
   548 	CExtension *p = new CLocalExtension(path);
   549 
   550 	/* We put us in the list beforehand so extensions that check for each other
   551 	 * won't recursively load each other.
   552 	 */
   553 	m_Libs.push_back(p);
   554 
   555 	if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
   556 	{
   557 		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
   558 		p->SetError(error);
   559 	}
   560 
   561 	return p;
   562 }
   563 
   564 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
   565 {
   566 	List<CExtension *>::iterator iter;
   567 	CExtension *pExt;
   568 
   569 	/* Chomp off the path */
   570 	char lookup[PLATFORM_MAX_PATH];
   571 	g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
   572 
   573 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   574 	{
   575 		pExt = (*iter);
   576 		char short_file[PLATFORM_MAX_PATH];
   577 		g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename());
   578 		if (strcmp(lookup, short_file) == 0)
   579 		{
   580 			return pExt;
   581 		}
   582 	}
   583 
   584 	/* If we got no results, test if there was a platform extension.
   585 	 * If not, add one.
   586 	 */
   587 	if (!strstr(file, "." PLATFORM_LIB_EXT))
   588 	{
   589 		char path[PLATFORM_MAX_PATH];
   590 		UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT);
   591 		return FindExtensionByFile(path);
   592 	}
   593 
   594 	return NULL;
   595 }
   596 
   597 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
   598 {
   599 	List<CExtension *>::iterator iter;
   600 	CExtension *pExt;
   601 	IExtensionInterface *pAPI;
   602 	const char *name;
   603 
   604 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   605 	{
   606 		pExt = (*iter);
   607 		if (!pExt->IsLoaded())
   608 		{
   609 			continue;
   610 		}
   611 		if ((pAPI = pExt->GetAPI()) == NULL)
   612 		{
   613 			continue;
   614 		}
   615 		name = pAPI->GetExtensionName();
   616 		if (!name)
   617 		{
   618 			continue;
   619 		}
   620 		if (strcmp(name, ext) == 0)
   621 		{
   622 			return pExt;
   623 		}
   624 	}
   625 
   626 	return NULL;
   627 }
   628 
   629 IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
   630 {
   631 	IExtension *pAlready;
   632 	if ((pAlready=FindExtensionByFile(file)) != NULL)
   633 	{
   634 		return pAlready;
   635 	}
   636 
   637 	CExtension *pExt = new CLocalExtension(file);
   638 
   639 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   640 	{
   641 		pExt->Unload();
   642 		delete pExt;
   643 		return NULL;
   644 	}
   645 
   646 	/* :TODO: do QueryRunning check if the map is loaded */
   647 
   648 	m_Libs.push_back(pExt);
   649 
   650 	return pExt;
   651 }
   652 
   653 void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo)
   654 {
   655 	CExtension *pExt = (CExtension *)pRequester;
   656 	CExtension *pOwner = (CExtension *)pInfo->owner;
   657 
   658 	pExt->AddDependency(pInfo);
   659 
   660 	IExtensionInterface *pAPI = pExt->GetAPI();
   661 	if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface))
   662 	{
   663 		pOwner->AddChildDependent(pExt, pInfo->iface);
   664 	}
   665 }
   666 
   667 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   668 {
   669 	CExtension *pExt = (CExtension *)pOwner;
   670 
   671 	pExt->AddInterface(pInterface);
   672 }
   673 
   674 void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
   675 {
   676 	CExtension *pExt = (CExtension *)pParent;
   677 
   678 	pExt->AddPlugin(pPlugin);
   679 }
   680 
   681 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   682 {
   683 	List<CExtension *>::iterator iter;
   684 
   685 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   686 	{
   687 		(*iter)->RemovePlugin(plugin);
   688 	}
   689 }
   690 
   691 CExtension *CExtensionManager::FindByOrder(unsigned int num)
   692 {
   693 	if (num < 1 || num > m_Libs.size())
   694 	{
   695 		return NULL;
   696 	}
   697 
   698 	List<CExtension *>::iterator iter = m_Libs.begin();
   699 
   700 	while (iter != m_Libs.end())
   701 	{
   702 		if (--num == 0)
   703 		{
   704 			return (*iter);
   705 		}
   706 		iter++;
   707 	}
   708 
   709 	return NULL;
   710 }
   711 
   712 void CExtensionManager::BindAllNativesToPlugin(IPlugin *pPlugin)
   713 {
   714 	IPluginContext *pContext = pPlugin->GetBaseContext();
   715 
   716 	List<CExtension *> exts;
   717 
   718 	uint32_t natives = pContext->GetNativesNum();
   719 	sp_native_t *native;
   720 	sm_extnative_t *x_native;
   721 	sm_repnative_t *r_native;
   722 	for (uint32_t i=0; i<natives; i++)
   723 	{
   724 		/* Make sure the native is valid */
   725 		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
   726 		{
   727 			continue;
   728 		}
   729 
   730 		/* Make sure the native is not already bound */
   731 		if (native->status == SP_NATIVE_BOUND)
   732 		{
   733 			/* If it is bound, see if there is a replacement. */
   734 			if ((r_native = m_RepNatives.retrieve(native->name)) == NULL)
   735 			{
   736 				continue;
   737 			}
   738 
   739 			/* Rewrite the address.  Whee! */
   740 			native->pfn = r_native->info.func;
   741 
   742 			/* Make sure this will unload safely */
   743 			WeakNative wn((CPlugin *)pPlugin, i);
   744 			r_native->owner->m_ReplacedNatives.push_back(wn);
   745 
   746 			continue;
   747 		}
   748 
   749 		/* See if we've got this native in our cache */
   750 		if ((x_native = m_ExtNatives.retrieve(native->name)) == NULL)
   751 		{
   752 			continue;
   753 		}
   754 		/* Try and bind it */
   755 		if (pContext->BindNativeToIndex(i, x_native->info->func) != SP_ERROR_NONE)
   756 		{
   757 			continue;
   758 		}
   759 		/* See if it's optional */
   760 		if (native->flags & SP_NTVFLAG_OPTIONAL)
   761 		{
   762 			WeakNative wkn = WeakNative((CPlugin *)pPlugin, i);
   763 			x_native->owner->m_WeakNatives.push_back(wkn);
   764 		}
   765 		else if (exts.find(x_native->owner) == exts.end())
   766 		{
   767 			exts.push_back(x_native->owner);
   768 		}
   769 	}
   770 
   771 	List<CExtension *>::iterator iter;
   772 	for (iter = exts.begin(); iter != exts.end(); iter++)
   773 	{
   774 		CExtension *pExt = (*iter);
   775 		if (pExt->m_Plugins.find(pPlugin) != pExt->m_Plugins.end())
   776 		{
   777 			continue;
   778 		}
   779 		pExt->m_Plugins.push_back(pPlugin);
   780 	}
   781 }
   782 
   783 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   784 {
   785 	if (!_pExt)
   786 	{
   787 		return false;
   788 	}
   789 
   790 	CExtension *pExt = (CExtension *)_pExt;
   791 
   792 	if (m_Libs.find(pExt) == m_Libs.end())
   793 	{
   794 		return false;
   795 	}
   796 
   797 	/* First remove us from internal lists */
   798 	g_ShareSys.RemoveInterfaces(_pExt);
   799 	m_Libs.remove(pExt);
   800 
   801 	List<CExtension *> UnloadQueue;
   802 
   803 	/* Handle dependencies */
   804 	if (pExt->IsLoaded())
   805 	{
   806 		/* Unload any dependent plugins */
   807 		List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
   808 		while (p_iter != pExt->m_Plugins.end())
   809 		{
   810 			/* We have to manually unlink ourselves here, since we're no longer being managed */
   811 			g_PluginSys.UnloadPlugin((*p_iter));
   812 			p_iter = pExt->m_Plugins.erase(p_iter);
   813 		}
   814 
   815 		List<String>::iterator s_iter;
   816 		for (s_iter = pExt->m_Libraries.begin();
   817 			 s_iter != pExt->m_Libraries.end();
   818 			 s_iter++)
   819 		{
   820 			g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
   821 		}
   822 
   823 		/* Unbind weak natives */
   824 		List<WeakNative>::iterator wkn_iter;
   825 		for (wkn_iter=pExt->m_WeakNatives.begin(); wkn_iter!=pExt->m_WeakNatives.end(); wkn_iter++)
   826 		{
   827 			WeakNative & wkn = (*wkn_iter);
   828 			sp_context_t *ctx = wkn.pl->GetContext();
   829 			ctx->natives[wkn.idx].status = SP_NATIVE_UNBOUND;
   830 		}
   831 
   832 		/* Unbind replacement natives, link them back to their originals */
   833 		for (wkn_iter = pExt->m_ReplacedNatives.begin();
   834 			 wkn_iter != pExt->m_ReplacedNatives.end();
   835 			 wkn_iter++)
   836 		{
   837 			WeakNative & wkn = (*wkn_iter);
   838 			sp_context_t *ctx = wkn.pl->GetContext();
   839 			sm_repnative_t *r_native = m_RepNatives.retrieve(ctx->natives[wkn.idx].name);
   840 			if (r_native == NULL || ctx->natives[wkn.idx].pfn != r_native->info.func)
   841 			{
   842 				continue;
   843 			}
   844 			ctx->natives[wkn.idx].pfn = r_native->original;
   845 		}
   846 
   847 		/* Notify and/or unload all dependencies */
   848 		List<CExtension *>::iterator c_iter;
   849 		CExtension *pDep;
   850 		IExtensionInterface *pAPI;
   851 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   852 		{
   853 			pDep = (*c_iter);
   854 			if ((pAPI=pDep->GetAPI()) == NULL)
   855 			{
   856 				continue;
   857 			}
   858 			if (pDep == pExt)
   859 			{
   860 				continue;
   861 			}
   862 			/* Now, get its dependency list */
   863 			bool dropped = false;
   864 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   865 			while (i_iter != pDep->m_Deps.end())
   866 			{
   867 				if ((*i_iter).owner == _pExt)
   868 				{
   869 					if (!pAPI->QueryInterfaceDrop((*i_iter).iface))
   870 					{
   871 						if (!dropped)
   872 						{
   873 							dropped = true;
   874 							UnloadQueue.push_back(pDep);
   875 						}
   876 					}
   877 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   878 					i_iter = pDep->m_Deps.erase(i_iter);
   879 				}
   880 				else
   881 				{
   882 					i_iter++;
   883 				}
   884 			}
   885 			/* Flush out any back references to this plugin */
   886 			i_iter = pDep->m_ChildDeps.begin();
   887 			while (i_iter != pDep->m_ChildDeps.end())
   888 			{
   889 				if ((*i_iter).owner == pExt)
   890 				{
   891 					i_iter = pDep->m_ChildDeps.erase(i_iter);
   892 				}
   893 				else
   894 				{
   895 					i_iter++;
   896 				}
   897 			}
   898 		}
   899 
   900 		/* Unbind our natives from Core */
   901 		List<const sp_nativeinfo_t *>::iterator native_iter;
   902 		for (native_iter = pExt->m_Natives.begin();
   903 			 native_iter != pExt->m_Natives.end();
   904 			 native_iter++)
   905 		{
   906 			const sp_nativeinfo_t *natives = (*native_iter);
   907 			sm_extnative_t *x_native;
   908 			for (unsigned int n = 0;
   909 				 natives[n].name != NULL && natives[n].func != NULL;
   910 				 n++)
   911 			{
   912 				x_native = m_ExtNatives.retrieve(natives[n].name);
   913 				if (x_native && x_native->owner == pExt)
   914 				{
   915 					m_ExtNatives.remove(natives[n].name);
   916 				}
   917 			}
   918 		}
   919 
   920 		/* Unbind our replacement natives */
   921 		List<sm_repnative_t>::iterator rep_iter = m_RepNativeList.begin();
   922 		while (rep_iter != m_RepNativeList.end())
   923 		{
   924 			sm_repnative_t & r_native = (*rep_iter);
   925 			if (r_native.owner == pExt)
   926 			{
   927 				m_RepNatives.remove(r_native.info.name);
   928 				rep_iter = m_RepNativeList.erase(rep_iter);
   929 			}
   930 			else
   931 			{
   932 				rep_iter++;
   933 			}
   934 		}
   935 
   936 		/* Tell it to unload */
   937 		pAPI = pExt->GetAPI();
   938 		pAPI->OnExtensionUnload();
   939 	}
   940 
   941 	IdentityToken_t *pIdentity;
   942 	if ((pIdentity = pExt->GetIdentity()) != NULL)
   943 	{
   944 		SMGlobalClass *glob = SMGlobalClass::head;
   945 		while (glob)
   946 		{
   947 			glob->OnSourceModIdentityDropped(pIdentity);
   948 			glob = glob->m_pGlobalClassNext;
   949 		}
   950 	}
   951 
   952 	pExt->Unload();
   953 	delete pExt;
   954 
   955 	List<CExtension *>::iterator iter;
   956 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   957 	{
   958 		/* NOTE: This is safe because the unload function backs out of anything not present */
   959 		UnloadExtension((*iter));
   960 	}
   961 
   962 	return true;
   963 }
   964 
   965 void CExtensionManager::AddNatives(IExtension *pOwner, const sp_nativeinfo_t *natives)
   966 {
   967 	CExtension *pExt = (CExtension *)pOwner;
   968 
   969 	pExt->m_Natives.push_back(natives);
   970 
   971 	for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++)
   972 	{
   973 		if (m_ExtNatives.retrieve(natives[i].name) != NULL)
   974 		{
   975 			continue;
   976 		}
   977 		sm_extnative_t x_native;
   978 		x_native.info = &natives[i];
   979 		x_native.owner = pExt;
   980 		m_ExtNatives.insert(natives[i].name, x_native);
   981 	}
   982 }
   983 
   984 void CExtensionManager::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives)
   985 {
   986 	SPVM_NATIVE_FUNC orig;
   987 
   988 	for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++)
   989 	{
   990 		if ((orig = g_PluginSys.FindCoreNative(natives[i].name)) == NULL)
   991 		{
   992 			continue;
   993 		}
   994 		sm_repnative_t rep;
   995 		rep.info = natives[i];
   996 		rep.owner = (CExtension *)myself;
   997 		rep.original = orig;
   998 		m_RepNativeList.push_back(rep);
   999 		m_RepNatives.insert(natives[i].name, rep);
  1000 	}
  1001 }
  1002 
  1003 void CExtensionManager::MarkAllLoaded()
  1004 {
  1005 	List<CExtension *>::iterator iter;
  1006 	CExtension *pExt;
  1007 
  1008 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1009 	{
  1010 		pExt = (*iter);
  1011 		if (!pExt->IsLoaded())
  1012 		{
  1013 			continue;
  1014 		}
  1015 		if (pExt->m_bFullyLoaded)
  1016 		{
  1017 			continue;
  1018 		}
  1019 		pExt->MarkAllLoaded();
  1020 	}
  1021 }
  1022 
  1023 void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
  1024 {
  1025 	/* This function doesn't really need to do anything now.  We make sure the 
  1026 	 * other extension is loaded, but handling of dependencies is really done 
  1027 	 * by the interface fetcher.
  1028 	 */
  1029 	if (required || autoload)
  1030 	{
  1031 		LoadAutoExtension(file);
  1032 	}
  1033 }
  1034 
  1035 void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
  1036 {
  1037 	int argcount = command.ArgC();
  1038 	if (argcount >= 3)
  1039 	{
  1040 		const char *cmd = command.Arg(2);
  1041 		if (strcmp(cmd, "list") == 0)
  1042 		{
  1043 			List<CExtension *>::iterator iter;
  1044 			CExtension *pExt;
  1045 			unsigned int num = 1;
  1046 			switch (m_Libs.size())
  1047 			{
  1048 			case 1:
  1049 				{
  1050 					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
  1051 					break;
  1052 				}
  1053 			case 0:
  1054 				{
  1055 					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
  1056 					break;
  1057 				}
  1058 			default:
  1059 				{
  1060 					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
  1061 					break;
  1062 				}
  1063 			}
  1064 			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
  1065 			{
  1066 				pExt = (*iter);
  1067 				if (pExt->IsLoaded())
  1068 				{
  1069 					char error[255];
  1070 					if (!pExt->IsRunning(error, sizeof(error)))
  1071 					{
  1072 						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
  1073 					}
  1074 					else
  1075 					{
  1076 						IExtensionInterface *pAPI = pExt->GetAPI();
  1077 						const char *name = pAPI->GetExtensionName();
  1078 						const char *version = pAPI->GetExtensionVerString();
  1079 						const char *descr = pAPI->GetExtensionDescription();
  1080 						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
  1081 					}
  1082 				} else {
  1083 					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
  1084 				}
  1085 			}
  1086 			return;
  1087 		}
  1088 		else if (strcmp(cmd, "info") == 0)
  1089 		{
  1090 			if (argcount < 4)
  1091 			{
  1092 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
  1093 				return;
  1094 			}
  1095 
  1096 			const char *sId = command.Arg(3);
  1097 			unsigned int id = atoi(sId);
  1098 			if (id <= 0)
  1099 			{
  1100 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
  1101 				return;
  1102 			}
  1103 
  1104 			if (m_Libs.size() == 0)
  1105 			{
  1106 				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
  1107 				return;
  1108 			}
  1109 
  1110 			if (id > m_Libs.size())
  1111 			{
  1112 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
  1113 				return;
  1114 			}
  1115 
  1116 			List<CExtension *>::iterator iter = m_Libs.begin();
  1117 			CExtension *pExt = NULL;
  1118 			while (iter != m_Libs.end())
  1119 			{
  1120 				if (--id == 0)
  1121 				{
  1122 					pExt = (*iter);
  1123 					break;
  1124 				}
  1125 				iter++;
  1126 			}
  1127 			/* This should never happen */
  1128 			if (!pExt)
  1129 			{
  1130 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
  1131 				return;
  1132 			}
  1133 
  1134 			if (!pExt->IsLoaded())
  1135 			{
  1136 				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1137 				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
  1138 			}
  1139 			else
  1140 			{
  1141 				char error[255];
  1142 				if (!pExt->IsRunning(error, sizeof(error)))
  1143 				{
  1144 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1145 					g_RootMenu.ConsolePrint(" Loaded: Yes");
  1146 					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
  1147 				}
  1148 				else
  1149 				{
  1150 					IExtensionInterface *pAPI = pExt->GetAPI();
  1151 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1152 					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
  1153 					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
  1154 					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
  1155 					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
  1156 
  1157 					if (pExt->IsExternal())
  1158 					{
  1159 						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
  1160 					}
  1161 					else if (pAPI->IsMetamodExtension())
  1162 					{
  1163 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
  1164 					}
  1165 					else
  1166 					{
  1167 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
  1168 					}
  1169 				}
  1170 			}
  1171 			return;
  1172 		}
  1173 		else if (strcmp(cmd, "unload") == 0)
  1174 		{
  1175 			if (argcount < 4)
  1176 			{
  1177 				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
  1178 				return;
  1179 			}
  1180 
  1181 			const char *arg = command.Arg(3);
  1182 			unsigned int num = atoi(arg);
  1183 			CExtension *pExt = FindByOrder(num);
  1184 
  1185 			if (!pExt)
  1186 			{
  1187 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
  1188 				return;
  1189 			}
  1190 
  1191 			if (argcount > 4 && pExt->unload_code)
  1192 			{
  1193 				const char *unload = command.Arg(4);
  1194 				if (pExt->unload_code == (unsigned)atoi(unload))
  1195 				{
  1196 					char filename[PLATFORM_MAX_PATH];
  1197 					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1198 					UnloadExtension(pExt);
  1199 					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1200 				}
  1201 				else
  1202 				{
  1203 					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
  1204 				}
  1205 				return;
  1206 			}
  1207 
  1208 			if (!pExt->IsLoaded() 
  1209 				|| (!pExt->m_ChildDeps.size() && !pExt->m_Plugins.size()))
  1210 			{
  1211 				char filename[PLATFORM_MAX_PATH];
  1212 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1213 				UnloadExtension(pExt);
  1214 				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1215 				return;
  1216 			}
  1217 			else
  1218 			{
  1219 				List<IPlugin *> plugins;
  1220 				if (pExt->m_ChildDeps.size())
  1221 				{
  1222 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
  1223 					List<CExtension *>::iterator iter;
  1224 					CExtension *pOther;
  1225 					/* Get list of all extensions */
  1226 					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1227 					{
  1228 						List<IfaceInfo>::iterator i_iter;
  1229 						pOther = (*iter);
  1230 						if (!pOther->IsLoaded() || pOther == pExt)
  1231 						{
  1232 							continue;
  1233 						}
  1234 						/* Get their dependencies */
  1235 						for (i_iter=pOther->m_Deps.begin();
  1236 							 i_iter!=pOther->m_Deps.end();
  1237 							 i_iter++)
  1238 						{
  1239 							/* Is this dependency to us? */
  1240 							if ((*i_iter).owner != pExt)
  1241 							{
  1242 								continue;
  1243 							}
  1244 							/* Will our dependent care? */
  1245 							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
  1246 							{
  1247 								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
  1248 								/* Add to plugin unload list */
  1249 								List<IPlugin *>::iterator p_iter;
  1250 								for (p_iter=pOther->m_Plugins.begin();
  1251 									 p_iter!=pOther->m_Plugins.end();
  1252 									 p_iter++)
  1253 								{
  1254 									if (plugins.find((*p_iter)) == plugins.end())
  1255 									{
  1256 										plugins.push_back((*p_iter));
  1257 									}
  1258 								}
  1259 							}
  1260 						}
  1261 					}
  1262 				}
  1263 				if (pExt->m_Plugins.size())
  1264 				{
  1265 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
  1266 					List<IPlugin *>::iterator iter;
  1267 					IPlugin *pPlugin;
  1268 					for (iter = pExt->m_Plugins.begin(); iter != pExt->m_Plugins.end(); iter++)
  1269 					{
  1270 						pPlugin = (*iter);
  1271 						if (plugins.find(pPlugin) == plugins.end())
  1272 						{
  1273 							plugins.push_back(pPlugin);
  1274 						}
  1275 					}
  1276 					for (iter = plugins.begin(); iter != plugins.end(); iter++)
  1277 					{
  1278 						pPlugin = (*iter);
  1279 						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
  1280 					}
  1281 				}
  1282 				srand(static_cast<int>(time(NULL)));
  1283 				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
  1284 				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
  1285 				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
  1286 
  1287 				return;
  1288 			}
  1289 		}
  1290 	}
  1291 
  1292 	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
  1293 	g_RootMenu.DrawGenericOption("info", "Extra extension information");
  1294 	g_RootMenu.DrawGenericOption("list", "List extensions");
  1295 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
  1296 }
  1297 
  1298 CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
  1299 {
  1300 	if (ptr->type == g_ExtType)
  1301 	{
  1302 		return (CExtension *)(ptr->ptr);
  1303 	}
  1304 
  1305 	return NULL;
  1306 }
  1307 
  1308 CExtensionManager::CExtensionManager()
  1309 {
  1310 }
  1311 
  1312 CExtensionManager::~CExtensionManager()
  1313 {
  1314 }
  1315 
  1316 void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
  1317 {
  1318 	CExtension *pExt = (CExtension *)pSource;
  1319 	pExt->AddLibrary(library);
  1320 	g_PluginSys.OnLibraryAction(library, false, false);
  1321 }
  1322 
  1323 bool CExtensionManager::LibraryExists(const char *library)
  1324 {
  1325 	CExtension *pExt;
  1326 
  1327 	for (List<CExtension *>::iterator iter = m_Libs.begin();
  1328 		 iter != m_Libs.end();
  1329 		 iter++)
  1330 	{
  1331 		pExt = (*iter);
  1332 		for (List<String>::iterator s_iter = pExt->m_Libraries.begin();
  1333 			 s_iter != pExt->m_Libraries.end();
  1334 			 s_iter++)
  1335 		{
  1336 			if ((*s_iter).compare(library) == 0)
  1337 			{
  1338 				return true;
  1339 			}
  1340 		}
  1341 	}
  1342 
  1343 	return false;
  1344 }
  1345 
  1346 IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
  1347 											const char *filepath,
  1348 											const char *filename,
  1349 											char *error,
  1350 											size_t maxlength)
  1351 {
  1352 	IExtension *pAlready;
  1353 	if ((pAlready=FindExtensionByFile(filename)) != NULL)
  1354 	{
  1355 		return pAlready;
  1356 	}
  1357 
  1358 	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
  1359 
  1360 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
  1361 	{
  1362 		pExt->Unload();
  1363 		delete pExt;
  1364 		return NULL;
  1365 	}
  1366 
  1367 	m_Libs.push_back(pExt);
  1368 
  1369 	return pExt;
  1370 }
  1371