core/logic/ShareSys.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Oct 09 08:43:08 2013 -0400 (2013-10-09)
changeset 4139 de7d1cd00703
parent 4109 10eb68d668af
child 4358 52aa5fab9866
permissions -rw-r--r--
Move ForwardSys from core to logic (bug 5953, r=fyren).
dvander@370
     1
/**
dvander@4067
     2
 * vim: set ts=4 sw=4 tw=99 noet :
ds@1330
     3
 * =============================================================================
dvander@1231
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1330
     6
 * =============================================================================
dvander@370
     7
 *
ds@1330
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    10
 * Free Software Foundation.
dvander@1231
    11
 * 
ds@1330
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    15
 * details.
dvander@1231
    16
 *
ds@1330
    17
 * You should have received a copy of the GNU General Public License along with
ds@1330
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
ds@1330
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@370
    28
 *
dvander@370
    29
 * Version: $Id$
dvander@370
    30
 */
dvander@370
    31
dvander@229
    32
#include "ShareSys.h"
dvander@3872
    33
#include "ExtensionSys.h"
dvander@3872
    34
#include <ILibrarySys.h>
dvander@3872
    35
#include "common_logic.h"
dvander@3872
    36
#include "PluginSys.h"
dvander@229
    37
#include "HandleSys.h"
dvander@4106
    38
#include <assert.h>
dvander@229
    39
dvander@4109
    40
using namespace ke;
dvander@4109
    41
dvander@229
    42
ShareSystem g_ShareSys;
dvander@2086
    43
static unsigned int g_mark_serial = 0;
dvander@229
    44
dvander@229
    45
ShareSystem::ShareSystem()
dvander@229
    46
{
dvander@229
    47
	m_IdentRoot.ident = 0;
dvander@229
    48
	m_TypeRoot = 0;
dvander@229
    49
	m_IfaceType = 0;
dvander@229
    50
	m_CoreType = 0;
dvander@229
    51
}
dvander@229
    52
dvander@229
    53
IdentityToken_t *ShareSystem::CreateCoreIdentity()
dvander@229
    54
{
dvander@229
    55
	if (!m_CoreType)
dvander@229
    56
	{
dvander@229
    57
		m_CoreType = CreateIdentType("CORE");
dvander@229
    58
	}
dvander@229
    59
dvander@868
    60
	return CreateIdentity(m_CoreType, this);
dvander@229
    61
}
dvander@229
    62
dvander@1770
    63
void ShareSystem::Initialize()
dvander@229
    64
{
dvander@262
    65
	TypeAccess sec;
dvander@229
    66
dvander@3872
    67
	handlesys->InitAccessDefaults(&sec, NULL);
dvander@262
    68
	sec.ident = GetIdentRoot();
dvander@262
    69
dvander@3872
    70
	m_TypeRoot = handlesys->CreateType("Identity", this, 0, &sec, NULL, NULL, NULL);
dvander@3872
    71
	m_IfaceType = handlesys->CreateType("Interface", this, 0, NULL, NULL, GetIdentRoot(), NULL);
dvander@229
    72
dvander@229
    73
	/* Initialize our static identity handle */
dvander@3872
    74
	m_IdentRoot.ident = handlesys->CreateHandle(m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL);
dvander@305
    75
dvander@336
    76
	/* Add the Handle System and others... they are too innocent and pure to do it themselves */
dvander@3872
    77
	AddInterface(NULL, handlesys);
dvander@3872
    78
	AddInterface(NULL, libsys);
dvander@229
    79
}
dvander@229
    80
dvander@229
    81
void ShareSystem::OnSourceModShutdown()
dvander@229
    82
{
dvander@229
    83
	if (m_CoreType)
dvander@229
    84
	{
dvander@3872
    85
		handlesys->RemoveType(m_CoreType, GetIdentRoot());
dvander@229
    86
	}
dvander@229
    87
dvander@3872
    88
	handlesys->RemoveType(m_IfaceType, GetIdentRoot());
dvander@3872
    89
	handlesys->RemoveType(m_TypeRoot, GetIdentRoot());
dvander@229
    90
}
dvander@229
    91
dvander@229
    92
IdentityType_t ShareSystem::FindIdentType(const char *name)
dvander@229
    93
{
dvander@229
    94
	HandleType_t type;
dvander@229
    95
dvander@3872
    96
	if (handlesys->FindHandleType(name, &type))
dvander@229
    97
	{
dvander@229
    98
		if (g_HandleSys.TypeCheck(type, m_TypeRoot))
dvander@229
    99
		{
dvander@229
   100
			return type;
dvander@229
   101
		}
dvander@229
   102
	}
dvander@229
   103
dvander@229
   104
	return 0;
dvander@229
   105
}
dvander@229
   106
dvander@229
   107
IdentityType_t ShareSystem::CreateIdentType(const char *name)
dvander@229
   108
{
dvander@229
   109
	if (!m_TypeRoot)
dvander@229
   110
	{
dvander@229
   111
		return 0;
dvander@229
   112
	}
dvander@229
   113
dvander@3872
   114
	return handlesys->CreateType(name, this, m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL);
dvander@229
   115
}
dvander@229
   116
dvander@229
   117
void ShareSystem::OnHandleDestroy(HandleType_t type, void *object)
dvander@229
   118
{
dvander@283
   119
	/* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */
dvander@229
   120
}
dvander@229
   121
dvander@868
   122
IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr)
dvander@229
   123
{
dvander@229
   124
	if (!m_TypeRoot)
dvander@229
   125
	{
dvander@229
   126
		return 0;
dvander@229
   127
	}
dvander@229
   128
dvander@229
   129
	/* :TODO: Cache? */
dvander@229
   130
	IdentityToken_t *pToken = new IdentityToken_t;
dvander@528
   131
dvander@528
   132
	HandleSecurity sec;
dvander@528
   133
	sec.pOwner = sec.pIdentity = GetIdentRoot();
dvander@528
   134
dvander@528
   135
	pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true);
dvander@868
   136
	pToken->ptr = ptr;
dvander@868
   137
	pToken->type = type;
dvander@229
   138
dvander@229
   139
	return pToken;
dvander@229
   140
}
dvander@229
   141
dvander@304
   142
bool ShareSystem::AddInterface(IExtension *myself, SMInterface *iface)
dvander@229
   143
{
dvander@229
   144
	if (!iface)
dvander@229
   145
	{
dvander@229
   146
		return false;
dvander@229
   147
	}
dvander@229
   148
dvander@229
   149
	IfaceInfo info;
dvander@229
   150
dvander@304
   151
	info.owner = myself;
dvander@229
   152
	info.iface = iface;
dvander@229
   153
	
dvander@229
   154
	m_Interfaces.push_back(info);
dvander@229
   155
dvander@229
   156
	return true;
dvander@229
   157
}
dvander@229
   158
dvander@229
   159
bool ShareSystem::RequestInterface(const char *iface_name, 
dvander@229
   160
								   unsigned int iface_vers, 
dvander@311
   161
								   IExtension *myself, 
dvander@229
   162
								   SMInterface **pIface)
dvander@229
   163
{
dvander@229
   164
	/* See if the interface exists */
dvander@229
   165
	List<IfaceInfo>::iterator iter;
dvander@229
   166
	SMInterface *iface;
dvander@304
   167
	IExtension *iface_owner;
dvander@229
   168
	bool found = false;
dvander@229
   169
	for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++)
dvander@229
   170
	{
dvander@229
   171
		IfaceInfo &info = (*iter);
dvander@229
   172
		iface = info.iface;
dvander@229
   173
		if (strcmp(iface->GetInterfaceName(), iface_name) == 0)
dvander@229
   174
		{
dvander@229
   175
			if (iface->GetInterfaceVersion() == iface_vers
dvander@229
   176
				|| iface->IsVersionCompatible(iface_vers))
dvander@229
   177
			{
dvander@304
   178
				iface_owner = info.owner;
dvander@229
   179
				found = true;
dvander@229
   180
				break;
dvander@229
   181
			}
dvander@229
   182
		}
dvander@229
   183
	}
dvander@229
   184
dvander@229
   185
	if (!found)
dvander@229
   186
	{
dvander@229
   187
		return false;
dvander@229
   188
	}
dvander@229
   189
dvander@304
   190
	/* Add a dependency node */
dvander@229
   191
	if (iface_owner)
dvander@229
   192
	{
dvander@304
   193
		IfaceInfo info;
dvander@304
   194
		info.iface = iface;
dvander@304
   195
		info.owner = iface_owner;
dvander@311
   196
		g_Extensions.BindDependency(myself, &info);
dvander@229
   197
	}
dvander@229
   198
dvander@304
   199
	if (pIface)
dvander@304
   200
	{
dvander@304
   201
		*pIface = iface;
dvander@304
   202
	}
dvander@229
   203
dvander@304
   204
	return true;
dvander@229
   205
}
dvander@229
   206
dvander@312
   207
void ShareSystem::AddNatives(IExtension *myself, const sp_nativeinfo_t *natives)
dvander@229
   208
{
dvander@2086
   209
	CNativeOwner *pOwner;
dvander@2086
   210
dvander@2086
   211
	pOwner = g_Extensions.GetNativeOwner(myself);
dvander@2086
   212
dvander@2086
   213
	pOwner->AddNatives(natives);
dvander@229
   214
}
dvander@229
   215
dvander@229
   216
void ShareSystem::DestroyIdentity(IdentityToken_t *identity)
dvander@229
   217
{
dvander@262
   218
	HandleSecurity sec;
dvander@262
   219
dvander@262
   220
	sec.pOwner = GetIdentRoot();
dvander@262
   221
	sec.pIdentity = GetIdentRoot();
dvander@262
   222
dvander@3872
   223
	handlesys->FreeHandle(identity->ident, &sec);
dvander@229
   224
	delete identity;
dvander@229
   225
}
dvander@229
   226
dvander@229
   227
void ShareSystem::DestroyIdentType(IdentityType_t type)
dvander@229
   228
{
dvander@3872
   229
	handlesys->RemoveType(type, GetIdentRoot());
dvander@229
   230
}
dvander@229
   231
dvander@304
   232
void ShareSystem::RemoveInterfaces(IExtension *pExtension)
dvander@304
   233
{
dvander@304
   234
	List<IfaceInfo>::iterator iter = m_Interfaces.begin();
dvander@304
   235
dvander@304
   236
	while (iter != m_Interfaces.end())
dvander@304
   237
	{
dvander@304
   238
		if ((*iter).owner == pExtension)
dvander@304
   239
		{
dvander@304
   240
			iter = m_Interfaces.erase(iter);
dvander@2086
   241
		}
dvander@2086
   242
		else
dvander@2086
   243
		{
dvander@304
   244
			iter++;
dvander@304
   245
		}
dvander@304
   246
	}
dvander@304
   247
}
dvander@324
   248
dvander@324
   249
void ShareSystem::AddDependency(IExtension *myself, const char *filename, bool require, bool autoload)
dvander@324
   250
{
dvander@324
   251
	g_Extensions.AddDependency(myself, filename, require, autoload);
dvander@324
   252
}
dvander@324
   253
dvander@1572
   254
void ShareSystem::RegisterLibrary(IExtension *myself, const char *name)
dvander@1572
   255
{
dvander@1572
   256
	g_Extensions.AddLibrary(myself, name);
dvander@1572
   257
}
dvander@1689
   258
dvander@1689
   259
void ShareSystem::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives)
dvander@1689
   260
{
dvander@4106
   261
	assert(false);
dvander@2086
   262
}
dvander@2086
   263
dvander@4109
   264
PassRef<Native> ShareSystem::FindNative(const char *name)
dvander@2086
   265
{
dvander@4109
   266
	NativeCache::Result r = m_NtvCache.find(name);
dvander@4109
   267
	if (!r.found())
dvander@2086
   268
		return NULL;
dvander@4109
   269
	return *r;
dvander@2086
   270
}
dvander@2086
   271
dvander@2086
   272
void ShareSystem::BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly)
dvander@2086
   273
{
dvander@2086
   274
	sp_native_t *native;
dvander@2086
   275
	uint32_t i, native_count;
dvander@2086
   276
	IPluginContext *pContext;
dvander@2086
   277
dvander@2086
   278
	pContext = pPlugin->GetBaseContext();
dvander@2086
   279
dvander@2086
   280
	/* Generate a new serial ID, mark our dependencies with it. */
dvander@2086
   281
	g_mark_serial++;
dvander@3872
   282
	pPlugin->PropagateMarkSerial(g_mark_serial);
dvander@2086
   283
dvander@2086
   284
	native_count = pContext->GetNativesNum();
dvander@2086
   285
	for (i = 0; i < native_count; i++)
dvander@2086
   286
	{
dvander@2086
   287
		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
dvander@2086
   288
			continue;
dvander@2086
   289
dvander@4106
   290
		// If we're already bound, no need to do anything else.
dvander@2086
   291
		if (native->status == SP_NATIVE_BOUND)
dvander@4106
   292
			continue;
dvander@4106
   293
dvander@2086
   294
		/* Otherwise, the native must be in our cache. */
dvander@4109
   295
		Ref<Native> pEntry = FindNative(native->name);
dvander@4106
   296
		if (!pEntry)
dvander@2086
   297
			continue;
dvander@2086
   298
dvander@3872
   299
		if (bCoreOnly && pEntry->owner != &g_CoreNatives)
dvander@2086
   300
			continue;
dvander@2086
   301
dvander@2086
   302
		BindNativeToPlugin(pPlugin, native, i, pEntry);
dvander@2086
   303
	}
dvander@2086
   304
}
dvander@2086
   305
dvander@4109
   306
void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, const Ref<Native> &entry)
dvander@2086
   307
{
dvander@4109
   308
	if (!entry->owner)
dvander@4109
   309
		return;
dvander@2086
   310
dvander@4109
   311
	IPluginContext *pContext = pPlugin->GetBaseContext();
dvander@2086
   312
dvander@4109
   313
	uint32_t i;
dvander@4109
   314
	if (pContext->FindNativeByName(entry->name(), &i) != SP_ERROR_NONE)
dvander@2086
   315
		return;
dvander@4109
   316
dvander@4109
   317
	sp_native_t *native;
dvander@2086
   318
	if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
dvander@2086
   319
		return;
dvander@2086
   320
dvander@2086
   321
	if (native->status == SP_NATIVE_BOUND)
dvander@2086
   322
		return;
dvander@2086
   323
dvander@4109
   324
	BindNativeToPlugin(pPlugin, native, i, entry);
dvander@1689
   325
}
dvander@2086
   326
dvander@4109
   327
void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, sp_native_t *native, uint32_t index,
dvander@4109
   328
                                     const Ref<Native> &pEntry)
dvander@2086
   329
{
dvander@2086
   330
	/* Mark as bound... we do the rest next. */
dvander@2086
   331
	native->status = SP_NATIVE_BOUND;
dvander@4108
   332
	native->pfn = pEntry->func();
dvander@4106
   333
dvander@4106
   334
	/* We don't bother with dependency crap if the owner is Core. */
dvander@4106
   335
	if (pEntry->owner != &g_CoreNatives)
dvander@2086
   336
	{
dvander@4106
   337
		/* The native is optional, this is a special case */
dvander@4106
   338
		if ((native->flags & SP_NTVFLAG_OPTIONAL) == SP_NTVFLAG_OPTIONAL)
dvander@2086
   339
		{
dvander@4106
   340
			/* Only add if there is a valid owner. */
dvander@4109
   341
			if (pEntry->owner)
dvander@4106
   342
				pEntry->owner->AddWeakRef(WeakNative(pPlugin, index));
dvander@2086
   343
			else
dvander@4106
   344
				native->status = SP_NATIVE_UNBOUND;
dvander@4106
   345
		}
dvander@4106
   346
		/* Otherwise, we're a strong dependent and not a weak one */
dvander@4106
   347
		else
dvander@4106
   348
		{
dvander@4106
   349
			/* See if this has already been marked as a dependent.
dvander@4106
   350
			 * If it has, it means this relationship has already occurred, 
dvander@4106
   351
			 * and there is no reason to do it again.
dvander@4106
   352
			 */
dvander@4106
   353
			if (pEntry->owner != pPlugin->ToNativeOwner() 
dvander@4106
   354
				&& pEntry->owner->GetMarkSerial() != g_mark_serial)
dvander@4106
   355
			{
dvander@4106
   356
				/* This has not been marked as a dependency yet */
dvander@4106
   357
				//pPlugin->AddDependency(pEntry->owner);
dvander@4106
   358
				pEntry->owner->AddDependent(pPlugin);
dvander@4106
   359
				pEntry->owner->SetMarkSerial(g_mark_serial);
dvander@2086
   360
			}
dvander@2086
   361
		}
dvander@2086
   362
	}
dvander@2086
   363
}
dvander@2086
   364
dvander@4109
   365
PassRef<Native> ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv)
dvander@2086
   366
{
dvander@4109
   367
	NativeCache::Insert i = m_NtvCache.findForAdd(ntv->name);
dvander@4109
   368
	if (i.found())
dvander@2086
   369
		return NULL;
dvander@2086
   370
dvander@4109
   371
	Ref<Native> entry = Newborn<Native>(new Native(pOwner, ntv));
dvander@4109
   372
	m_NtvCache.insert(ntv->name, entry);
dvander@4109
   373
	return entry;
dvander@4108
   374
}
dvander@2086
   375
dvander@4108
   376
FakeNative::~FakeNative()
dvander@4108
   377
{
dvander@4108
   378
	g_pSourcePawn2->DestroyFakeNative(gate);
dvander@2086
   379
}
dvander@2086
   380
dvander@2086
   381
void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name)
dvander@2086
   382
{
dvander@4109
   383
	NativeCache::Result r = m_NtvCache.find(name);
dvander@4109
   384
	if (!r.found())
dvander@2086
   385
		return;
dvander@2086
   386
dvander@4109
   387
	Ref<Native> entry(*r);
dvander@4109
   388
	if (entry->owner != pOwner)
dvander@2086
   389
		return;
dvander@2086
   390
dvander@4109
   391
	// Clear out the owner bit as a sanity measure.
dvander@4109
   392
	entry->owner = NULL;
dvander@4109
   393
dvander@4109
   394
	m_NtvCache.remove(r);
dvander@2086
   395
}
dvander@2086
   396
dvander@4109
   397
PassRef<Native> ShareSystem::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
dvander@2086
   398
{
dvander@4109
   399
	Ref<Native> entry(FindNative(name));
dvander@4109
   400
	if (entry)
dvander@2086
   401
		return NULL;
dvander@2086
   402
dvander@4109
   403
	AutoPtr<FakeNative> fake(new FakeNative(name, pFunc));
dvander@2086
   404
dvander@4108
   405
	fake->gate = g_pSourcePawn2->CreateFakeNative(func, fake);
dvander@4108
   406
	if (!fake->gate)
dvander@2086
   407
		return NULL;
dvander@2086
   408
dvander@4108
   409
	CNativeOwner *owner = g_PluginSys.GetPluginByCtx(fake->ctx->GetContext());
dvander@2086
   410
dvander@4109
   411
	entry = Newborn<Native>(new Native(owner, fake.take()));
dvander@4109
   412
	m_NtvCache.insert(name, entry);
dvander@2086
   413
dvander@4109
   414
	return entry;
dvander@2086
   415
}
dvander@2841
   416
dvander@2841
   417
void ShareSystem::AddCapabilityProvider(IExtension *myself, IFeatureProvider *provider,
dvander@2841
   418
		                                const char *name)
dvander@2841
   419
{
dvander@4067
   420
	if (m_caps.contains(name))
dvander@2841
   421
		return;
dvander@2841
   422
dvander@2841
   423
	Capability cap;
dvander@2841
   424
	cap.ext = myself;
dvander@2841
   425
	cap.provider = provider;
dvander@2841
   426
dvander@2841
   427
	m_caps.insert(name, cap);
dvander@2841
   428
}
dvander@2841
   429
dvander@2841
   430
void ShareSystem::DropCapabilityProvider(IExtension *myself, IFeatureProvider *provider,
dvander@2841
   431
		                                 const char *name)
dvander@2841
   432
{
dvander@4067
   433
	StringHashMap<Capability>::Result r = m_caps.find(name);
dvander@4067
   434
	if (!r.found())
dvander@4067
   435
		return;
dvander@4067
   436
	if (r->value.ext != myself || r->value.provider != provider)
dvander@2841
   437
		return;
dvander@2841
   438
dvander@4067
   439
	m_caps.remove(r);
dvander@2841
   440
}
dvander@2841
   441
dvander@2841
   442
FeatureStatus ShareSystem::TestFeature(IPluginRuntime *pRuntime, FeatureType feature, 
dvander@2841
   443
                                       const char *name)
dvander@2841
   444
{
dvander@2841
   445
	switch (feature)
dvander@2841
   446
	{
dvander@2841
   447
	case FeatureType_Native:
dvander@2841
   448
		return TestNative(pRuntime, name);
dvander@2841
   449
	case FeatureType_Capability:
dvander@2841
   450
		return TestCap(name);
dvander@2841
   451
	default:
dvander@2841
   452
		break;
dvander@2841
   453
	}
dvander@2841
   454
dvander@2841
   455
	return FeatureStatus_Unknown;
dvander@2841
   456
}
dvander@2841
   457
dvander@2841
   458
FeatureStatus ShareSystem::TestNative(IPluginRuntime *pRuntime, const char *name)
dvander@2841
   459
{
dvander@2841
   460
	uint32_t index;
dvander@2841
   461
dvander@2841
   462
	if (pRuntime->FindNativeByName(name, &index) == SP_ERROR_NONE)
dvander@2841
   463
	{
dvander@2841
   464
		sp_native_t *native;
dvander@2841
   465
		if (pRuntime->GetNativeByIndex(index, &native) == SP_ERROR_NONE)
dvander@2841
   466
		{
dvander@2841
   467
			if (native->status == SP_NATIVE_BOUND)
dvander@2841
   468
				return FeatureStatus_Available;
dvander@2841
   469
			else
dvander@2841
   470
				return FeatureStatus_Unknown;
dvander@2841
   471
		}
dvander@2841
   472
	}
dvander@2841
   473
dvander@4109
   474
	Ref<Native> entry = FindNative(name);
dvander@4106
   475
	if (!entry)
dvander@2841
   476
		return FeatureStatus_Unknown;
dvander@2841
   477
dvander@4106
   478
	return FeatureStatus_Unavailable;
dvander@2841
   479
}
dvander@2841
   480
dvander@2841
   481
FeatureStatus ShareSystem::TestCap(const char *name)
dvander@2841
   482
{
dvander@4067
   483
	StringHashMap<Capability>::Result r = m_caps.find(name);
dvander@4067
   484
	if (!r.found())
dvander@2841
   485
		return FeatureStatus_Unknown;
dvander@2841
   486
dvander@4067
   487
	return r->value.provider->GetFeatureStatus(FeatureType_Capability, name);
dvander@2841
   488
}