dvander@298: #include "ExtensionSys.h" dvander@298: #include "LibrarySys.h" dvander@298: #include "ShareSys.h" dvander@298: #include "CLogger.h" dvander@304: #include "sourcemm_api.h" dvander@298: dvander@298: CExtensionManager g_Extensions; dvander@298: IdentityType_t g_ExtType; dvander@298: dvander@298: CExtension::CExtension(const char *filename, char *error, size_t err_max) dvander@298: { dvander@298: m_File.assign(filename); dvander@298: m_pAPI = NULL; dvander@298: m_pIdentToken = NULL; dvander@304: m_PlId = 0; dvander@298: dvander@298: char path[PLATFORM_MAX_PATH+1]; dvander@298: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename); dvander@298: dvander@298: m_pLib = g_LibSys.OpenLibrary(path, error, err_max); dvander@298: dvander@298: if (m_pLib == NULL) dvander@298: { dvander@298: return; dvander@298: } dvander@298: dvander@298: typedef IExtensionInterface *(*GETAPI)(); dvander@298: GETAPI pfnGetAPI = NULL; dvander@298: dvander@298: if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@298: snprintf(error, err_max, "Unable to find extension entry point"); dvander@298: return; dvander@298: } dvander@298: dvander@298: m_pAPI = pfnGetAPI(); dvander@298: if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@298: snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); dvander@298: return; dvander@298: } dvander@298: dvander@298: if (m_pAPI->IsMetamodExtension()) dvander@298: { dvander@304: bool already; dvander@304: m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max); dvander@298: } dvander@298: dvander@298: m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType); dvander@298: dvander@298: if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, g_SourceMod.IsLateLoadInMap())) dvander@298: { dvander@298: if (m_pAPI->IsMetamodExtension()) dvander@298: { dvander@304: if (m_PlId) dvander@304: { dvander@304: char dummy[255]; dvander@304: g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy)); dvander@304: m_PlId = 0; dvander@304: } dvander@298: } dvander@298: m_pAPI = NULL; dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@298: g_ShareSys.DestroyIdentity(m_pIdentToken); dvander@298: m_pIdentToken = NULL; dvander@298: return; dvander@298: } dvander@298: } dvander@298: dvander@298: CExtension::~CExtension() dvander@298: { dvander@298: if (m_pAPI) dvander@298: { dvander@298: m_pAPI->OnExtensionUnload(); dvander@304: if (m_PlId) dvander@304: { dvander@304: g_pMMPlugins->Unload(m_PlId, true, NULL, 0); dvander@304: } dvander@298: } dvander@298: dvander@298: if (m_pIdentToken) dvander@298: { dvander@298: g_ShareSys.DestroyIdentity(m_pIdentToken); dvander@298: } dvander@298: dvander@298: if (m_pLib) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: } dvander@298: } dvander@298: dvander@298: void CExtension::SetError(const char *error) dvander@298: { dvander@298: m_Error.assign(error); dvander@298: } dvander@298: dvander@298: IExtensionInterface *CExtension::GetAPI() dvander@298: { dvander@298: return m_pAPI; dvander@298: } dvander@298: dvander@298: const char *CExtension::GetFilename() dvander@298: { dvander@298: return m_File.c_str(); dvander@298: } dvander@298: dvander@298: IdentityToken_t *CExtension::GetIdentity() dvander@298: { dvander@298: return m_pIdentToken; dvander@298: } dvander@298: dvander@298: bool CExtension::IsLoaded() dvander@298: { dvander@298: return (m_pLib != NULL); dvander@298: } dvander@298: dvander@304: void CExtension::AddDependency(IfaceInfo *pInfo) dvander@304: { dvander@304: m_Deps.push_back(*pInfo); dvander@304: } dvander@304: dvander@304: ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) dvander@304: { dvander@304: List::iterator iter = m_Deps.begin(); dvander@304: dvander@304: if (iter == m_Deps.end()) dvander@304: { dvander@304: return NULL; dvander@304: } dvander@304: dvander@304: if (pOwner) dvander@304: { dvander@304: *pOwner = (*iter).owner; dvander@304: } dvander@304: if (pInterface) dvander@304: { dvander@304: *pInterface = (*iter).iface; dvander@304: } dvander@304: dvander@304: List::iterator *pIter = new List::iterator(iter); dvander@304: dvander@304: return (ITERATOR *)pIter; dvander@304: } dvander@304: dvander@304: bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) dvander@304: { dvander@304: List::iterator *pIter = (List::iterator *)iter; dvander@304: List::iterator _iter; dvander@304: dvander@304: if (_iter == m_Deps.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: _iter++; dvander@304: dvander@304: if (pOwner) dvander@304: { dvander@304: *pOwner = (*_iter).owner; dvander@304: } dvander@304: if (pInterface) dvander@304: { dvander@304: *pInterface = (*_iter).iface; dvander@304: } dvander@304: dvander@304: *pIter = _iter; dvander@304: dvander@304: if (_iter == m_Deps.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: return true; dvander@304: } dvander@304: dvander@304: void CExtension::FreeDependencyIterator(ITERATOR *iter) dvander@304: { dvander@304: List::iterator *pIter = (List::iterator *)iter; dvander@304: dvander@304: delete pIter; dvander@304: } dvander@304: dvander@304: void CExtension::AddInterface(SMInterface *pInterface) dvander@304: { dvander@304: m_Interfaces.push_back(pInterface); dvander@304: } dvander@304: dvander@304: /********************* dvander@304: * EXTENSION MANAGER * dvander@304: *********************/ dvander@304: dvander@298: void CExtensionManager::OnSourceModAllInitialized() dvander@298: { dvander@298: g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); dvander@298: } dvander@298: dvander@298: void CExtensionManager::OnSourceModShutdown() dvander@298: { dvander@298: g_ShareSys.DestroyIdentType(g_ExtType); dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::LoadAutoExtension(const char *path) dvander@298: { dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(path)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: dvander@298: char error[256]; dvander@298: CExtension *p = new CExtension(path, error, sizeof(error)); dvander@298: dvander@298: if (!p->IsLoaded()) dvander@298: { dvander@298: g_Logger.LogError("[SOURCEMOD] Unable to load extension \"%s\": %s", path, error); dvander@298: p->SetError(error); dvander@298: } dvander@298: dvander@298: m_Libs.push_back(p); dvander@298: dvander@298: return p; dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::FindExtensionByFile(const char *file) dvander@298: { dvander@298: List::iterator iter; dvander@298: CExtension *pExt; dvander@298: dvander@298: /* Make sure the file direction is right */ dvander@298: char path[PLATFORM_MAX_PATH+1]; dvander@298: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); dvander@298: dvander@298: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@298: { dvander@298: pExt = (*iter); dvander@298: if (strcmp(pExt->GetFilename(), path) == 0) dvander@298: { dvander@298: return pExt; dvander@298: } dvander@298: } dvander@298: dvander@298: return NULL; dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::FindExtensionByName(const char *ext) dvander@298: { dvander@298: List::iterator iter; dvander@298: CExtension *pExt; dvander@298: IExtensionInterface *pAPI; dvander@298: const char *name; dvander@298: dvander@298: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@298: { dvander@298: pExt = (*iter); dvander@298: if (!pExt->IsLoaded()) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if ((pAPI = pExt->GetAPI()) == NULL) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: name = pAPI->GetExtensionName(); dvander@298: if (!name) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if (strcmp(name, ext) == 0) dvander@298: { dvander@298: return pExt; dvander@298: } dvander@298: } dvander@298: dvander@298: return NULL; dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max) dvander@298: { dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(file)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: dvander@298: CExtension *pExt = new CExtension(file, error, err_max); dvander@298: dvander@298: /* :NOTE: lifetime is currently ignored */ dvander@298: dvander@298: if (!pExt->IsLoaded()) dvander@298: { dvander@298: delete pExt; dvander@298: return NULL; dvander@298: } dvander@298: dvander@298: m_Libs.push_back(pExt); dvander@298: dvander@298: return pExt; dvander@298: } dvander@298: dvander@304: void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo) dvander@298: { dvander@304: CExtension *pExt = (CExtension *)pOwner; dvander@304: dvander@304: pExt->AddDependency(pInfo); dvander@304: } dvander@304: dvander@304: void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) dvander@304: { dvander@304: CExtension *pExt = (CExtension *)pOwner; dvander@304: dvander@304: pExt->AddInterface(pInterface); dvander@298: } dvander@298: dvander@304: bool CExtensionManager::UnloadExtension(IExtension *_pExt) dvander@298: { dvander@304: if (!_pExt) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: CExtension *pExt = (CExtension *)_pExt; dvander@304: dvander@304: if (m_Libs.find(pExt) == m_Libs.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: /* First remove us from internal lists */ dvander@304: g_ShareSys.RemoveInterfaces(_pExt); dvander@304: m_Libs.remove(pExt); dvander@304: dvander@304: List UnloadQueue; dvander@298: dvander@304: /* Handle dependencies */ dvander@304: if (pExt->IsLoaded()) dvander@304: { dvander@304: /* Notify and/or unload all dependencies */ dvander@304: List::iterator c_iter; dvander@304: CExtension *pDep; dvander@304: IExtensionInterface *pAPI; dvander@304: for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) dvander@304: { dvander@304: pDep = (*c_iter); dvander@304: if ((pAPI=pDep->GetAPI()) == NULL) dvander@304: { dvander@304: continue; dvander@304: } dvander@304: /* Now, get its dependency list */ dvander@304: bool dropped = false; dvander@304: List::iterator i_iter = pDep->m_Deps.begin(); dvander@304: while (i_iter != pDep->m_Deps.end()) dvander@304: { dvander@304: if ((*i_iter).owner == _pExt) dvander@304: { dvander@304: if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface)) dvander@304: { dvander@304: dropped = true; dvander@304: } dvander@304: pAPI->NotifyInterfaceDrop((*i_iter).iface); dvander@304: i_iter = pDep->m_Deps.erase(i_iter); dvander@304: } else { dvander@304: i_iter++; dvander@304: } dvander@304: } dvander@304: } dvander@304: } dvander@304: dvander@304: List::iterator iter; dvander@304: for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) dvander@304: { dvander@304: /* NOTE: This is safe because the unload function backs out of anything not present */ dvander@304: UnloadExtension((*iter)); dvander@304: } dvander@304: dvander@298: return true; dvander@298: }