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