more eol-style
[xonotic/netradiant.git] / libs / synapse / synapse.cpp
index f6477849e3726c35290396bc84209b657854acf4..432a02c614bad0ffcf3b9efbdd5d486ea4109d1a 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-#include <assert.h>\r
-\r
-// seems to be required for str.h\r
-#include <glib.h>\r
-\r
-#include "synapse.h"\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  #include <dirent.h>\r
-#endif\r
-\r
-/*\r
-===================================================\r
-diagnostic stuff\r
-===================================================\r
-*/\r
-\r
-extern "C"\r
-{\r
-\r
-static PFN_SYN_PRINTF_VA g_pPrintf = NULL;  \r
-  \r
-void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf)\r
-{\r
-  g_pPrintf = pf;\r
-}\r
-\r
-#define BUFFER_SIZE 4096\r
-\r
-void Syn_Printf (const char *text, ...)\r
-{\r
-  char buf[BUFFER_SIZE];\r
-  va_list args;\r
-  \r
-  if (!text)\r
-    return;\r
-  \r
-  if (g_pPrintf)\r
-  {\r
-    va_start (args, text);\r
-    (*g_pPrintf)(text, args);\r
-    va_end (args);\r
-  }\r
-  else\r
-  {\r
-    va_start (args, text);\r
-    vsnprintf (buf, BUFFER_SIZE, text, args);\r
-    buf[BUFFER_SIZE-1] = 0;\r
-    printf(buf);\r
-    va_end (args);\r
-  }\r
-}\r
-\r
-}\r
-\r
-/*\r
-=======================================================================\r
-server\r
-=======================================================================\r
-*/\r
-\r
-// this must be kept in sync with EAPIType\r
-static const char* APITypeName[4] =\r
-{\r
-  "SYN_UNKNOWN",\r
-  "SYN_PROVIDE",\r
-  "SYN_REQUIRE",\r
-  "SYN_REQUIRE_ANY"\r
-};\r
-\r
-CSynapseServer::CSynapseServer()\r
-{\r
-  mpDoc = NULL;\r
-  m_api_name = NULL;\r
-  m_content = NULL;\r
-  mpFocusedNode = NULL;\r
-}\r
-\r
-CSynapseServer::~CSynapseServer()\r
-{\r
-  if (m_api_name)\r
-    xmlFree(m_api_name);\r
-  if (m_content)\r
-    g_free(m_content);\r
-  Syn_Printf("TODO: free API managers\n");\r
-}\r
-\r
-void CSynapseServer::AddSearchPath(char* path)\r
-{\r
-  char *pLocalPath = new char[strlen(path)+1];\r
-  strcpy(pLocalPath, path);\r
-  mSearchPaths.push_front(pLocalPath);\r
-}\r
-\r
-bool CSynapseServer::Initialize(const char* conf_file, PFN_SYN_PRINTF_VA pf)\r
-{\r
-  // browse the paths to locate all potential modules\r
-  \r
-  Set_Syn_Printf(pf);\r
-  \r
-  if (conf_file)\r
-  {\r
-    // if a config file is specified and we fail to load it, we fail\r
-    Syn_Printf("loading synapse XML config file '%s'\n", conf_file);\r
-    mpDoc = xmlParseFile(conf_file);\r
-    if (!mpDoc)\r
-    {\r
-      Syn_Printf("'%s' invalid/not found\n", conf_file);\r
-      return false;\r
-    }    \r
-  }\r
-\r
-  for (list<char *>::iterator iPath=mSearchPaths.begin(); iPath!=mSearchPaths.end(); iPath++)\r
-  {\r
-    const char* path = *iPath;\r
-\r
-    Syn_Printf("Synapse Scanning modules path: %s\n", path);\r
-\r
-    GDir* dir = g_dir_open (path, 0, NULL);\r
-\r
-    if (dir != NULL)\r
-    {\r
-      while (1)\r
-      {\r
-        const gchar* name = g_dir_read_name(dir);\r
-        if(name == NULL)\r
-          break;\r
-\r
-        // too small to be isolated in win32/ and linux/ directories..\r
-#if defined(_WIN32)\r
-        const char* ext_so = ".dll";\r
-#elif defined (__linux__) || defined (__APPLE__)\r
-        const char* ext_so = ".so";\r
-#endif\r
-        const char* ext = strrchr (name, '.');\r
-        if ((ext == NULL) || (stricmp (ext, ext_so) != 0))\r
-          continue;\r
-\r
-        Str newModule;\r
-        newModule.Format("%s%s", path, name);\r
-        Syn_Printf("Found '%s'\n", newModule.GetBuffer());      \r
-        EnumerateInterfaces(newModule);\r
-      }\r
-\r
-      g_dir_close(dir);\r
-    }\r
-  }\r
-  return true;\r
-}\r
-\r
-#if defined(_WIN32)  \r
-#define FORMAT_BUFSIZE 2048\r
-const char* CSynapseServer::FormatGetLastError()\r
-{\r
-  static char buf[FORMAT_BUFSIZE];\r
-  FormatMessage(\r
-    FORMAT_MESSAGE_FROM_SYSTEM | \r
-    FORMAT_MESSAGE_IGNORE_INSERTS,\r
-    NULL,\r
-    GetLastError(),\r
-    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
-    buf,\r
-    FORMAT_BUFSIZE,\r
-    NULL \r
-  );\r
-  return buf;\r
-}\r
-\r
-void CSynapseServer::EnumerateInterfaces(Str &soname)\r
-{\r
-  CSynapseClientSlot slot;\r
-  slot.mpDLL = LoadLibrary(soname.GetBuffer());\r
-  if (!slot.mpDLL)\r
-  {\r
-    Syn_Printf("LoadLibrary '%s' failed\n", soname.GetBuffer());\r
-    Syn_Printf("  GetLastError: %s", FormatGetLastError());\r
-    return;\r
-  }\r
-  slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)GetProcAddress(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);\r
-  if (!slot.mpEnumerate)\r
-  {\r
-    Syn_Printf("GetProcAddress('%s') failed\n", NAME_SYNAPSE_ENUMERATEINTERFACES);\r
-    Syn_Printf("  GetLastError: %s", FormatGetLastError());\r
-    return;\r
-  }\r
-  Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());\r
-  slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);\r
-  if (!slot.mpClient)\r
-  {\r
-    Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());\r
-    if (!FreeLibrary(slot.mpDLL))\r
-    {\r
-      Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());\r
-    }\r
-    return;\r
-  }\r
-  slot.mFileName = soname;\r
-  mClients.push_front(slot);\r
-}\r
-\r
-void CSynapseClientSlot::ReleaseSO()\r
-{\r
-  if (!mpDLL)\r
-  {\r
-    Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());\r
-    return;\r
-  }\r
-  Syn_Printf("FreeLibrary '%s'\n", mpClient->GetInfo());\r
-  if (!FreeLibrary(mpDLL))\r
-  {\r
-    Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());\r
-  }\r
-  mpDLL = NULL;\r
-}\r
-\r
-#elif defined(__linux__) || defined (__APPLE__)\r
-void CSynapseServer::EnumerateInterfaces(Str &soname)\r
-{\r
-  CSynapseClientSlot slot;\r
-  slot.mpDLL = dlopen (soname.GetBuffer(), RTLD_NOW);\r
-  PFN_SYNAPSE_ENUMERATEINTERFACES *pEnumerate;\r
-  if (!slot.mpDLL)\r
-  {\r
-    char* error;\r
-    if ((error = (char *)dlerror()) == NULL)\r
-      error = "Unknown";\r
-    Syn_Printf("dlopen '%s' failed\n  dlerror: '%s'\n", soname.GetBuffer(), error);\r
-    return;\r
-  }\r
-  slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)dlsym(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);\r
-  if (!slot.mpEnumerate)\r
-  {\r
-    char* error;\r
-    if ((error = (char *)dlerror()) == NULL)\r
-      error = "Unknown";\r
-    Syn_Printf("dlsym '%s' failed on shared object '%s'\n  dlerror: '%s'\n", NAME_SYNAPSE_ENUMERATEINTERFACES, soname.GetBuffer(), error);\r
-    return;\r
-  }\r
-  Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());\r
-  slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);\r
-  if (!slot.mpClient)\r
-  {\r
-    Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());\r
-    if (dlclose(slot.mpDLL))\r
-    {\r
-      char* error;\r
-      if ((error = (char *)dlerror()) == NULL)\r
-        error = "Unknown";\r
-      Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);\r
-    }\r
-    return;\r
-  }\r
-  slot.mFileName = soname;\r
-  mClients.push_front(slot);\r
-}\r
-\r
-void CSynapseClientSlot::ReleaseSO()\r
-{\r
-  if (!mpDLL)\r
-  {\r
-    Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());\r
-    return;\r
-  }\r
-  Syn_Printf("dlclose '%s'\n", mpClient->GetInfo());\r
-  if (dlclose(mpDLL))\r
-  {\r
-    char* error;\r
-    if ((error = (char *)dlerror()) == NULL)\r
-      error = "Unknown";\r
-    Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);\r
-  }\r
-  mpDLL = NULL;\r
-}\r
-\r
-#endif\r
-\r
-void CSynapseServer::EnumerateBuiltinModule(CSynapseBuiltinClient *pClient)\r
-{\r
-  CSynapseClientSlot slot;\r
-  pClient->EnumerateInterfaces(this);\r
-  slot.mpClient = pClient;\r
-  slot.mType = SYN_BUILTIN;\r
-  mClients.push_front(slot);\r
-}\r
-\r
-PFN_SYN_PRINTF_VA CSynapseServer::Get_Syn_Printf()\r
-{\r
-  return g_pPrintf;\r
-}\r
-\r
-void CSynapseServer::TryPushStack(APIDescriptor_t *pAPI)\r
-{\r
-  list<APIDescriptor_t*>::iterator iAPI;\r
-  for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++)\r
-  {\r
-    if ((*iAPI) == pAPI)\r
-    {\r
-      return;\r
-    }\r
-  }\r
-  mStack.push_front(pAPI);      \r
-  mbStackChanged = true;  \r
-}\r
-\r
-list<CSynapseClientSlot>::iterator CSynapseServer::ShutdownClient(list<CSynapseClientSlot>::iterator iSlot)\r
-{\r
-  CSynapseClientSlot *pClientSlot = &(*iSlot);\r
-  if (pClientSlot->mpClient->IsActive())\r
-  {\r
-    // this should not happen except during core shutdown (i.e. editor is shutting down)\r
-    Syn_Printf("WARNING: ShutdownClient attempted on an active module '%s'\n", pClientSlot->mpClient->GetInfo());\r
-  }\r
-  // cleanup mStack\r
-  int i,api_count;\r
-  api_count = pClientSlot->mpClient->GetAPICount();\r
-  for(i=0; i<api_count; i++)\r
-  {\r
-    APIDescriptor_t *pAPI = pClientSlot->mpClient->GetAPIDescriptor(i);\r
-    // search this API in mStack\r
-    list< APIDescriptor_t *>::iterator iStack = mStack.begin();\r
-    while(iStack != mStack.end())\r
-    {\r
-      if (*iStack == pAPI)\r
-        break;\r
-      iStack++;\r
-    }\r
-    if (iStack != mStack.end())\r
-    {\r
-      if (pAPI->mType == SYN_REQUIRE)\r
-      {\r
-        if (pAPI->mbTableInitDone)\r
-        {\r
-          // even if non active, some SYN_REQUIRE may have been filled up\r
-          // look for the corresponding SYN_PROVIDE and decref\r
-          list< APIDescriptor_t *>::iterator iStackRequire = mStack.begin();\r
-          APIDescriptor_t *pMatchAPI;\r
-          while (iStackRequire != mStack.end())\r
-          {\r
-            pMatchAPI = *iStackRequire;\r
-            if ( pMatchAPI->mType == SYN_PROVIDE && MatchAPI( pMatchAPI, pAPI ) )\r
-              break;\r
-            iStackRequire++;\r
-          }\r
-          if (iStackRequire != mStack.end())\r
-          {\r
-            // we have found the corresponding SYN_PROVIDE\r
-            pMatchAPI->mRefCount--;\r
-          }\r
-          else\r
-          {\r
-            // this is not supposed to happen at all\r
-            Syn_Printf("ERROR: couldn't find the SYN_PROVIDE for an initialized SYN_REQUIRE API '%s' '%s' '%s'\n", pAPI->major_name, pAPI->minor_name, pClientSlot->mpClient->GetInfo());\r
-          }\r
-        }\r
-      }      \r
-      else if (pAPI->mType == SYN_PROVIDE)\r
-      {\r
-        // this should never happen on non active clients, it may happen during a core shutdown though\r
-        // if the mRefCount is != 0, that means there is at least a function table out there that will segfault things\r
-        Syn_Printf("WARNING: found a SYN_PROVIDE API '%s' '%s' with refcount %d in CSynapseServer::ShutdownClient for '%s'\n", pAPI->major_name, pAPI->minor_name, pAPI->mRefCount, pClientSlot->mpClient->GetInfo());\r
-      }\r
-      // mostly safe to remove it now\r
-      mStack.erase(iStack);\r
-    }\r
-  }\r
-  // we can actually release the module now\r
-  // NOTE: do we want to have a 'final shutdown' call to the client? (not as long as we don't have a use for it)\r
-  if (pClientSlot->mType == SYN_SO)\r
-  {\r
-    pClientSlot->ReleaseSO();\r
-  }  \r
-  return mClients.erase(iSlot);\r
-}\r
-\r
-void CSynapseServer::PushRequired(CSynapseClient *pClient)\r
-{\r
-  /* walk through the standard APIs and push them in */\r
-  int i,max = pClient->GetAPICount();  \r
-  for(i=0; i<max; i++)\r
-  {\r
-    APIDescriptor_t* pAPI = pClient->GetAPIDescriptor(i);\r
-    if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)\r
-    {\r
-      TryPushStack(pAPI);\r
-    }\r
-  }\r
-  \r
-  /* if this client has 'List' API Manager types, walk through them for addition too */\r
-  max = pClient->GetManagerListCount();\r
-  for(i=0; i<max; i++)\r
-  {\r
-    CSynapseAPIManager *pManager = pClient->GetManagerList(i);\r
-    assert(pManager->GetType() == API_LIST);\r
-    pManager->InitializeAPIList();\r
-    int j;\r
-    for(j=0; j<pManager->GetAPICount(); j++)\r
-    {\r
-      TryPushStack(pManager->GetAPI(j));\r
-    }\r
-  }\r
-  \r
-  /* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces\r
-   * and let them decide which ones they might want\r
-   */\r
-  \r
-  max = pClient->GetManagerMatchCount();\r
-\r
-  for(i=0; i<max; i++)\r
-  {\r
-    CSynapseAPIManager *pManager = pClient->GetManagerMatch(i);\r
-    // start matching all known SYN_PROVIDE APIs against this manager\r
-    list<CSynapseClientSlot>::iterator iClientSlot;\r
-    for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++)\r
-    {\r
-      CSynapseClient *pScanClient = (*iClientSlot).\r
-      mpClient;\r
-      int j,jmax = pScanClient->GetAPICount();\r
-      for(j=0; j<jmax; j++)\r
-      {\r
-        APIDescriptor_t *pAPI = pScanClient->GetAPIDescriptor(j);\r
-        if (pAPI->mType == SYN_PROVIDE)\r
-        {\r
-          if (pManager->MatchAPI(pAPI->major_name, pAPI->minor_name))\r
-          {\r
-            /*! we are going to want to load this one\r
-               * NOTE TTimo: what if this can not be resolved in the end?\r
-               * if this happens, then the whole startup will fail instead\r
-               * or we can use SYN_REQUIRE_ANY and drop it without consequences\r
-               */\r
-            APIDescriptor_t *pPushAPI = pManager->BuildRequireAPI(pAPI);\r
-            TryPushStack(pPushAPI);\r
-          }\r
-        }\r
-      }\r
-    }\r
-  }\r
-}\r
-\r
-bool CSynapseServer::MatchAPI(APIDescriptor_t *p1, APIDescriptor_t *p2)\r
-{\r
-  return MatchAPI( p1->major_name, p1->minor_name, p2->major_name, p2->minor_name );\r
-}\r
-\r
-bool CSynapseServer::MatchAPI(const char* major1, const char* minor1, const char* major2, const char* minor2)\r
-{\r
-  if ( strcmp( major1, major2 ) ) {\r
-    return false;\r
-  }\r
-  // either no minor at all for this API, or matching\r
-  if ( ( minor1 && minor2 ) && !strcmp( minor1, minor2 ) ) {\r
-    return true;\r
-  }\r
-  // or one of the minors says "*" (knowing that the majors match already)\r
-  if ( ( minor1 && !strcmp( minor1, "*" ) ) || ( minor2 && !strcmp( minor2, "*" ) ) ) {\r
-    return true;\r
-  }\r
-  return false;\r
-}\r
-\r
-bool CSynapseServer::ResolveAPI(APIDescriptor_t* pAPI)\r
-{\r
-  //Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);  \r
-  // loop through active clients, search for a client providing what we are looking for\r
-  list<CSynapseClientSlot>::iterator iClient;\r
-  for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)\r
-  {\r
-    // walk through interfaces on this client for a match\r
-    CSynapseClient *pScanClient = (*iClient).mpClient;\r
-    int i,max = pScanClient->GetAPICount();\r
-    for(i=0; i<max; i++)\r
-    {\r
-      APIDescriptor_t *pScanClientAPI = pScanClient->GetAPIDescriptor(i);\r
-      if (pScanClientAPI->mType == SYN_PROVIDE)\r
-      {\r
-        if (MatchAPI(pAPI, pScanClientAPI))\r
-        {\r
-          // can this client provide APIs yet\r
-          // it is possible that all of it's APIs have been filled and it's not been activated yet\r
-          pScanClient->CheckSetActive();\r
-          if (pScanClient->IsActive())\r
-          {\r
-            // make sure this interface has correct size (this is our version check)\r
-            if (pAPI->mSize != pScanClientAPI->mSize)\r
-            {\r
-              Syn_Printf("ERROR: version mismatch for API '%s' '%s' found in '%s' (size %d != %d)\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI->mSize, pScanClientAPI->mSize);\r
-              Syn_Printf("  the module and the server are incompatible\n");\r
-              // keep going to other APIs\r
-              continue;\r
-            }\r
-            // this is an active client, we can request\r
-            #ifdef SYNAPSE_VERBOSE\r
-              Syn_Printf("RequestAPI '%s' '%s' from '%s' for API %p\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI);\r
-            #endif\r
-            if (!pScanClient->RequestAPI(pAPI))\r
-            {\r
-              // this should never happen, means we think this module provides the API, but it answers that it doesn't\r
-              Syn_Printf("ERROR: RequestAPI failed\n");\r
-              return false;\r
-            }\r
-            pScanClientAPI->mRefCount++;\r
-            pAPI->mbTableInitDone = true;\r
-            return true; // job done\r
-          }\r
-          else\r
-          {\r
-            // this client is not active yet, some of it's required interfaces are not filled in\r
-            PushRequired(pScanClient);\r
-            // we will exit the scan through the APIDescriptor of this client and look at other clients\r
-            break;\r
-          }\r
-        }\r
-      }\r
-    }  \r
-  }\r
-  return false;\r
-}\r
-\r
-bool CSynapseServer::DoResolve(CSynapseClient *pClient)\r
-{\r
-  list<CSynapseClientSlot>::iterator iSlot;\r
-  for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)\r
-  {\r
-    if ((*iSlot).mpClient == pClient)\r
-      break;\r
-  }\r
-  if (iSlot == mClients.end())\r
-  {\r
-    Syn_Printf("CSynapserServer::Resolve adding new client slot '%s'\n", pClient->GetInfo());\r
-    CSynapseClientSlot slot;\r
-    slot.mpClient = pClient;\r
-    slot.mFileName = "local client";\r
-    // make it active so we can request the interfaces already\r
-    pClient->ForceSetActive();\r
-    mClients.push_front(slot);\r
-  }\r
-  else\r
-  {\r
-    // make it active so we can request the interfaces already\r
-    (*iSlot).mpClient->ForceSetActive();\r
-  }\r
-\r
-  // push the interfaces that need to be resolved for this client  \r
-  // NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces\r
-  PushRequired(pClient);\r
-  // start resolving now\r
-  // working till the stack is emptied or till we reach a dead end situation\r
-  // we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions\r
-  list<APIDescriptor_t*>::iterator iCurrent;\r
-  mbStackChanged = true; // init to true so we try the first elem\r
-  while (!mStack.empty())\r
-  {\r
-    //DumpStack();\r
-    if (!mbStackChanged)\r
-    {\r
-      // the stack didn't change last loop \r
-      iCurrent++;\r
-      if (iCurrent==mStack.end())\r
-      {\r
-        Syn_Printf("ERROR: CSynapseServer::Resolve, failed to resolve\n");\r
-        DumpStack();\r
-        return false;\r
-      }\r
-      if (ResolveAPI(*iCurrent))\r
-      {\r
-        iCurrent = mStack.erase(iCurrent);\r
-        mbStackChanged = true;\r
-      }\r
-    }\r
-    else\r
-    {\r
-      // the stack changed at last loop\r
-      mbStackChanged = false;\r
-      iCurrent = mStack.begin();\r
-      if (ResolveAPI(*iCurrent))\r
-      {\r
-        iCurrent = mStack.erase(iCurrent);\r
-        mbStackChanged = true;\r
-      }\r
-    }\r
-  }\r
-  return true;\r
-}\r
-\r
-bool CSynapseServer::Resolve(CSynapseClient *pClient)\r
-{\r
-  bool ret = DoResolve(pClient);\r
-  list<CSynapseClientSlot>::iterator iClient;  \r
-  iClient = mClients.begin();\r
-  while(iClient != mClients.end())\r
-  {\r
-    CSynapseClient *pClient = (*iClient).mpClient;\r
-    if (!pClient->IsActive())\r
-    {\r
-      Syn_Printf("Unloading an unused module: '%s'\n", pClient->GetInfo());\r
-      iClient = ShutdownClient(iClient);\r
-    }\r
-    else\r
-      iClient++;\r
-  }\r
-  return ret;\r
-}\r
-\r
-void CSynapseServer::Shutdown()\r
-{\r
-  Syn_Printf("Synapse server core is shutting down\n");\r
-  // do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)\r
-  // we seperate the client shutdown calls from the dlclose cause that part is a clean decref / free situation whereas dlclose will break links without advice\r
-  list<CSynapseClientSlot>::iterator iClient;  \r
-  iClient = mClients.begin();\r
-  for(iClient = mClients.begin(); iClient != mClients.end(); iClient++)\r
-  {\r
-    (*iClient).mpClient->Shutdown();\r
-  }  \r
-  // now release them from the server's point of view\r
-  iClient = mClients.begin();\r
-  while(iClient != mClients.end())\r
-  {\r
-    iClient = ShutdownClient(iClient);\r
-  }  \r
-}\r
-\r
-void CSynapseServer::DumpStack()\r
-{\r
-  list<APIDescriptor_t*>::iterator iCurrent;\r
-  for(iCurrent=mStack.begin(); iCurrent != mStack.end(); iCurrent++)\r
-  {\r
-    APIDescriptor_t*pAPI = *iCurrent;\r
-    Syn_Printf("interface %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);\r
-  }\r
-}\r
-\r
-void CSynapseServer::DumpActiveClients()\r
-{\r
-  list<CSynapseClientSlot>::iterator iClient;  \r
-  for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)\r
-  {\r
-    CSynapseClient *pClient = (*iClient).mpClient;\r
-    Syn_Printf("%s", pClient->GetInfo());\r
-    if (pClient->IsActive())\r
-      Syn_Printf("\n");\r
-    else\r
-      Syn_Printf(" (not active)\n");\r
-  }\r
-}\r
-\r
-bool CSynapseServer::SelectClientConfig(const char *client_name)\r
-{\r
-  if (!mpDoc)\r
-    return false;\r
-  xmlNodePtr pNode = xmlDocGetRootElement(mpDoc);\r
-  if (!pNode)\r
-    return false;\r
-  // look for the client\r
-  pNode=pNode->children;\r
-  while(pNode)\r
-  {\r
-    if (pNode->type == XML_ELEMENT_NODE)\r
-    {\r
-      xmlChar *prop = xmlGetProp(pNode, (const xmlChar *)"name");\r
-      if (!strcmp((const char *)prop, client_name))\r
-      {\r
-        xmlFree(prop);\r
-        break;\r
-      }\r
-      xmlFree(prop);\r
-    }\r
-    pNode = pNode->next;\r
-  }\r
-  if (!pNode)\r
-    return false; // config you asked for isn't there\r
-  // focus\r
-  mpFocusedNode = pNode->children;\r
-  mpCurrentClientConfig = pNode;\r
-  return true;\r
-}\r
-\r
-bool CSynapseServer::GetNextConfig(char **api_name, char **minor)\r
-{\r
-  while(mpFocusedNode && mpFocusedNode->name)\r
-  {\r
-    if (mpFocusedNode->type == XML_ELEMENT_NODE && !strcmp((const char *)mpFocusedNode->name, "api"))\r
-    {\r
-      if (m_api_name)\r
-        xmlFree(m_api_name);\r
-      m_api_name = xmlGetProp(mpFocusedNode, (const xmlChar *)"name");\r
-      *api_name = (char *)m_api_name;\r
-      if (m_content)\r
-        g_free(m_content);\r
-      m_content = g_strdup((const gchar *)mpFocusedNode->children->content);\r
-      g_strstrip(m_content);\r
-      *minor = m_content;\r
-      mpFocusedNode = mpFocusedNode->next;\r
-      return true;\r
-    }\r
-    mpFocusedNode = mpFocusedNode->next;    \r
-  }\r
-  return false;\r
-}\r
-\r
-bool CSynapseServer::GetConfigForAPI( const char *api, char **minor ) {\r
-  xmlNodePtr pNode = mpCurrentClientConfig->children;\r
-  while ( pNode && pNode->name ) {\r
-    if ( pNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)pNode->name, "api" ) ) {\r
-      if ( m_api_name ) {\r
-        xmlFree( m_api_name );\r
-      }\r
-      m_api_name = xmlGetProp( pNode, (const xmlChar *)"name" );\r
-      if ( !strcmp( (const char *)m_api_name, api ) ) {\r
-        if ( m_content ) {\r
-          g_free( m_content );\r
-        }\r
-        m_content = g_strdup( (const gchar *)pNode->children->content );\r
-        g_strstrip( m_content );\r
-        *minor = m_content;\r
-        return true;\r
-      }\r
-    }\r
-    pNode = pNode->next;\r
-  }\r
-  return false;\r
-}\r
-\r
-const char *CSynapseServer::GetModuleFilename(CSynapseClient *pClient)\r
-{\r
-  list<CSynapseClientSlot>::iterator iSlot;\r
-  for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)\r
-  {\r
-    if ((*iSlot).mpClient == pClient)\r
-    {\r
-      if ((*iSlot).mType == SYN_BUILTIN)\r
-      {\r
-        return "";     // FIXME\r
-      }\r
-      else\r
-      {\r
-        return (*iSlot).mFileName;\r
-      }\r
-    }\r
-  }\r
-  return NULL;\r
-}\r
-\r
-/*\r
-=======================================================================\r
-client\r
-=======================================================================\r
-*/\r
-\r
-CSynapseClient::CSynapseClient()\r
-{\r
-}\r
-\r
-void CSynapseClient::Shutdown()\r
-{\r
-  vector<APIDescriptor_t *>::iterator iAPI;\r
-  for(iAPI=mAPIDescriptors.begin(); iAPI!=mAPIDescriptors.end(); iAPI++)\r
-  {\r
-    APIDescriptor_t *pAPI = *iAPI;\r
-    if (pAPI->mRefCount != 0)\r
-      Syn_Printf("WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s'\n", GetInfo(), pAPI->major_name, pAPI->minor_name);\r
-    else\r
-      delete pAPI;\r
-    *iAPI=NULL;\r
-  }\r
-  mAPIDescriptors.clear();\r
-  vector<CSynapseAPIManager *>::iterator iManager;\r
-  for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)\r
-  {\r
-    CSynapseAPIManager *pManager = *iManager;\r
-    pManager->DecRef();\r
-    *iManager = NULL;\r
-  }\r
-  mManagersList.clear();\r
-  for(iManager=mManagersMatch.begin(); iManager!=mManagersMatch.end(); iManager++)\r
-  {\r
-    CSynapseAPIManager *pManager = *iManager;\r
-    pManager->DecRef();\r
-    *iManager = NULL;\r
-  }\r
-  mManagersMatch.clear();\r
-}\r
-\r
-CSynapseClient::~CSynapseClient()\r
-{\r
-  // this should not be doing anything when called from here if everything went right\r
-  // otherwise it's likely to crash .. at least that's the sign we missed something\r
-  Shutdown();\r
-}\r
-\r
-int CSynapseClient::GetAPICount()\r
-{\r
-  return mAPIDescriptors.size();\r
-}\r
-\r
-APIDescriptor_t* CSynapseClient::GetAPIDescriptor(int i)\r
-{\r
-  return mAPIDescriptors[i];\r
-}\r
-\r
-int CSynapseClient::GetManagerMatchCount()\r
-{\r
-  return mManagersMatch.size();\r
-}\r
-\r
-CSynapseAPIManager* CSynapseClient::GetManagerMatch(int i)\r
-{\r
-  return mManagersMatch[i];\r
-}\r
-\r
-int CSynapseClient::GetManagerListCount()\r
-{\r
-  return mManagersList.size();\r
-}\r
-\r
-CSynapseAPIManager* CSynapseClient::GetManagerList(int i)\r
-{\r
-  return mManagersList[i];\r
-}\r
-\r
-bool CSynapseClient::AddAPI(const char *major, const char *minor, int size, EAPIType type, void *pTable)\r
-{\r
-  // do some safe checks before actual addition\r
-  if (type == SYN_REQUIRE && !pTable)\r
-  {\r
-    Syn_Printf("ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer\n", major, minor, GetInfo());\r
-    return false;\r
-  }\r
-  if (pTable)\r
-  {\r
-    int *pi = (int *)pTable;\r
-    if (pi == 0)\r
-    {\r
-      Syn_Printf("ERROR: forgot to init function table size for interface '%s' '%s' from '%s'?\n", major, minor, GetInfo());\r
-      return false;\r
-    }\r
-  }\r
-  APIDescriptor_t *pAPI = new APIDescriptor_t;\r
-  memset(pAPI, 0, sizeof(APIDescriptor_t));\r
-  strncpy(pAPI->major_name, major, MAX_APINAME);\r
-  if (minor)\r
-    strncpy(pAPI->minor_name, minor, MAX_APINAME);\r
-  pAPI->mType = type;\r
-  pAPI->mpTable = pTable;\r
-  // store the interface size\r
-  if (type == SYN_PROVIDE)\r
-  {\r
-    if (size == 0)\r
-    {\r
-      Syn_Printf("ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s')\n", major, minor, GetInfo());\r
-      delete pAPI;\r
-      return false;\r
-    }\r
-    pAPI->mSize = size;\r
-  }\r
-  else if (type == SYN_REQUIRE)\r
-  {    \r
-    if (size != 0)\r
-    {\r
-      // if a non-zero value is given in function call, use this instead of the val in table\r
-      *((int *)pAPI->mpTable) = size;\r
-      pAPI->mSize = size;\r
-    }\r
-    else\r
-    {\r
-      pAPI->mSize = *((int *)pAPI->mpTable);\r
-      if (pAPI->mSize == 0)\r
-      {\r
-        Syn_Printf("ERROR: didn't get an interface size ('%s' '%s' from '%s')\n", major, minor, GetInfo());\r
-        delete pAPI;\r
-        return false;\r
-      }\r
-    }\r
-  }\r
-  else\r
-  {\r
-    Syn_Printf("ERROR: AddAPI type '%d' not supported\n", type);\r
-    return false;\r
-  }\r
-  mAPIDescriptors.push_back(pAPI);  \r
-  #ifdef SYNAPSE_VERBOSE\r
-    Syn_Printf("AddAPI: %s %p '%s' '%s' from '%s', size %d\n", APITypeName[pAPI->mType], pAPI, major, minor, GetInfo(), pAPI->mSize);\r
-  #endif\r
-  return true;\r
-}\r
-\r
-#include "version.h"\r
-\r
-const char* CSynapseClient::GetInfo()\r
-{\r
-  return "CSynapseClient built " __DATE__ " " RADIANT_VERSION;\r
-}\r
-\r
-bool CSynapseClient::CheckSetActive()\r
-{\r
-  if (mbActive)\r
-    return true;\r
-  int i,max=GetAPICount();\r
-  for(i=0; i<max; i++)\r
-  {\r
-    APIDescriptor_t *pAPI = GetAPIDescriptor(i);\r
-    if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)\r
-      return false;\r
-  }\r
-  // if we have managers with fixed list, those need to be completely filled in too\r
-  vector<CSynapseAPIManager *>::iterator iManager;\r
-  for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)\r
-  {\r
-    if (!(*iManager)->CheckSetActive())\r
-      return false; // one of the managers doesn't have all it needs yet\r
-  }\r
-  // call OnActivate to let the client perform last minute checks\r
-  // NOTE: this should be fatal instead of letting the engine try other combinations\r
-  if (!OnActivate()) {\r
-    return false;\r
-  }\r
-  // yes, all required interfaces have been initialized\r
-  Syn_Printf("'%s' activated\n", GetInfo());\r
-  mbActive = true;\r
-  return true;\r
-}\r
-\r
-bool CSynapseClient::ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] ) {\r
-  \r
-  if ( !client_name ) {\r
-    client_name = GetName();\r
-  }\r
-  \r
-  Syn_Printf("Dynamic APIs for client '%s'\n", GetInfo());\r
-  if ( !pServer->SelectClientConfig( client_name ) )\r
-  {\r
-    Syn_Printf( "Failed to select synapse client config '%s'\n", client_name );\r
-    return false;\r
-  }\r
-  \r
-  int i = 0;\r
-  while ( entries[i].type != SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty\r
-    char *minor;\r
-    if ( !pServer->GetConfigForAPI( entries[i].api, &minor ) ) {\r
-      Syn_Printf( "GetConfigForAPI '%s' failed - invalid XML config file?\n", entries[i].api );\r
-      return false;\r
-    }\r
-    AddAPI( entries[i].api, minor, entries[i].size, entries[i].type, entries[i].pTable );\r
-    i++;\r
-  }\r
-  Syn_Printf( "%d dynamic interfaces parsed for '%s'\n", i, client_name );\r
-  return true;\r
-}\r
-\r
-void CSynapseClient::AddManager(CSynapseAPIManager *pManager)\r
-{\r
-  pManager->IncRef();\r
-  if (pManager->GetType() == API_LIST)\r
-    mManagersList.push_back(pManager);\r
-  else\r
-    mManagersMatch.push_back(pManager);\r
-}\r
-\r
-CSynapseAPIManager::~CSynapseAPIManager()\r
-{\r
-  vector<APIDescriptor_t *>::iterator iAPI;\r
-  for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)\r
-  {\r
-    APIDescriptor_t *pAPI = *iAPI;\r
-    if (pAPI->mRefCount != 0)\r
-    {\r
-      Syn_Printf("WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s'\n", pAPI->major_name, pAPI->minor_name);\r
-    }\r
-    delete pAPI;\r
-    *iAPI = NULL;\r
-  }\r
-}\r
-\r
-APIDescriptor_t* CSynapseAPIManager::PrepareRequireAPI(APIDescriptor_t *pAPI)\r
-{\r
-#ifdef _DEBUG\r
-  if (pAPI->mType != SYN_PROVIDE)\r
-  {\r
-    Syn_Printf("ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI\n");\r
-    return NULL;\r
-  }\r
-#endif\r
-  APIDescriptor_t *pRequireAPI = new APIDescriptor_t;\r
-  memcpy(pRequireAPI, pAPI, sizeof(APIDescriptor_t));\r
-  pRequireAPI->mType = SYN_REQUIRE_ANY;\r
-  pRequireAPI->mpTable = NULL;\r
-  pRequireAPI->mbTableInitDone = false;\r
-  pRequireAPI->mSize = 0; // this will have to be set correctly by the child for version checking\r
-  pRequireAPI->mRefCount = 0;\r
-  return pRequireAPI;\r
-}\r
-\r
-void CSynapseAPIManager::SetMatchAPI(const char *major, const char *minor)\r
-{\r
-  if (strlen(minor)>MAX_PATTERN_STRING)\r
-  {\r
-    Syn_Printf("ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s'\n", minor);\r
-    return;\r
-  }\r
-  strcpy(major_pattern, major);\r
-  strcpy(minor_pattern, minor);\r
-  if (strcmp(minor, "*"))\r
-  {\r
-    mType = API_LIST;\r
-  }\r
-}\r
-\r
-bool CSynapseAPIManager::MatchAPI(const char *major, const char *minor)\r
-{\r
-  assert(mType == API_MATCH);\r
-  \r
-  /*!\r
-  if this interface has been allocated already, avoid requesting it again..\r
-  */\r
-  vector<APIDescriptor_t *>::iterator iAPI;\r
-  for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)\r
-  {\r
-    if (CSynapseServer::MatchAPI((*iAPI)->major_name, (*iAPI)->minor_name, major, minor))\r
-      return false;\r
-  }\r
-  \r
-  if (!strcmp(major, major_pattern))\r
-    return true;\r
-  return false;\r
-}\r
-\r
-bool CSynapseAPIManager::CheckSetActive()\r
-{\r
-  if (mType == API_MATCH)\r
-    return false;\r
-  // mType == API_LIST\r
-  int i,max = GetAPICount();\r
-  for(i=0; i<max; i++)\r
-  {\r
-    if (!GetAPI(i)->mbTableInitDone)\r
-      return false;\r
-  }\r
-  return true;\r
-}\r
-\r
-void CSynapseAPIManager::InitializeAPIList()\r
-{\r
-  char minor_tok[MAX_PATTERN_STRING];\r
-  char *token;\r
-  \r
-  if (mAPIs.size())\r
-  {\r
-    Syn_Printf("WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager\n");\r
-    return;\r
-  }\r
-  \r
-  strncpy(minor_tok, minor_pattern, MAX_PATTERN_STRING);\r
-  token = strtok(minor_tok, " ");\r
-  while (token)\r
-  {\r
-    /* ask the child to build from scratch */\r
-    APIDescriptor_t *pAPI = new APIDescriptor_t;\r
-    memset(pAPI, 0, sizeof(APIDescriptor_t));\r
-    strncpy(pAPI->major_name, major_pattern, MAX_APINAME);\r
-    strncpy(pAPI->minor_name, token, MAX_APINAME);\r
-    pAPI->mType = SYN_REQUIRE_ANY;\r
-    FillAPITable(pAPI);\r
-    mAPIs.push_back(pAPI);\r
-    token = strtok(NULL, " ");\r
-  }\r
-}\r
-  \r
-int CSynapseAPIManager::GetAPICount()\r
-{\r
-  return mAPIs.size();\r
-}\r
-\r
-APIDescriptor_t* CSynapseAPIManager::GetAPI(int i)\r
-{\r
-  return mAPIs[i];\r
-}\r
-\r
-// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=879\r
-void fini_stub() {\r
-  printf( "fini_stub\n" );\r
-}\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include <assert.h>
+
+// seems to be required for str.h
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "synapse.h"
+#if defined (__linux__) || defined (__APPLE__)
+  #include <dirent.h>
+#endif
+
+/*
+===================================================
+diagnostic stuff
+===================================================
+*/
+
+extern "C"
+{
+
+static PFN_SYN_PRINTF_VA g_pPrintf = NULL;  
+  
+void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf)
+{
+  g_pPrintf = pf;
+}
+
+#define BUFFER_SIZE 4096
+
+void Syn_Printf (const char *text, ...)
+{
+  char buf[BUFFER_SIZE];
+  va_list args;
+  
+  if (!text)
+    return;
+  
+  if (g_pPrintf)
+  {
+    va_start (args, text);
+    (*g_pPrintf)(text, args);
+    va_end (args);
+  }
+  else
+  {
+    va_start (args, text);
+    vsnprintf (buf, BUFFER_SIZE, text, args);
+    buf[BUFFER_SIZE-1] = 0;
+    printf(buf);
+    va_end (args);
+  }
+}
+
+}
+
+/*
+=======================================================================
+server
+=======================================================================
+*/
+
+// this must be kept in sync with EAPIType
+static const char* APITypeName[4] =
+{
+  "SYN_UNKNOWN",
+  "SYN_PROVIDE",
+  "SYN_REQUIRE",
+  "SYN_REQUIRE_ANY"
+};
+
+CSynapseServer::CSynapseServer()
+{
+  mpDoc = NULL;
+  m_api_name = NULL;
+  m_content = NULL;
+  mpFocusedNode = NULL;
+}
+
+CSynapseServer::~CSynapseServer()
+{
+  if (m_api_name)
+    xmlFree(m_api_name);
+  if (m_content)
+    g_free(m_content);
+  Syn_Printf("TODO: free API managers\n");
+}
+
+void CSynapseServer::AddSearchPath(char* path)
+{
+  char *pLocalPath = new char[strlen(path)+1];
+  strcpy(pLocalPath, path);
+  mSearchPaths.push_front(pLocalPath);
+}
+
+bool CSynapseServer::Initialize(const char* conf_file, PFN_SYN_PRINTF_VA pf)
+{
+  // browse the paths to locate all potential modules
+  
+  Set_Syn_Printf(pf);
+  
+  if (conf_file)
+  {
+    // if a config file is specified and we fail to load it, we fail
+    Syn_Printf("loading synapse XML config file '%s'\n", conf_file);
+    mpDoc = xmlParseFile(conf_file);
+    if (!mpDoc)
+    {
+      Syn_Printf("'%s' invalid/not found\n", conf_file);
+      return false;
+    }    
+  }
+
+  for (list<char *>::iterator iPath=mSearchPaths.begin(); iPath!=mSearchPaths.end(); iPath++)
+  {
+    const char* path = *iPath;
+
+    Syn_Printf("Synapse Scanning modules path: %s\n", path);
+
+    GDir* dir = g_dir_open (path, 0, NULL);
+
+    if (dir != NULL)
+    {
+      while (1)
+      {
+        const gchar* name = g_dir_read_name(dir);
+        if(name == NULL)
+          break;
+
+        // too small to be isolated in win32/ and linux/ directories..
+#if defined(_WIN32)
+        const char* ext_so = ".dll";
+#elif defined (__linux__) || defined (__APPLE__)
+        const char* ext_so = ".so";
+#endif
+        const char* ext = strrchr (name, '.');
+        if ((ext == NULL) || (stricmp (ext, ext_so) != 0))
+          continue;
+
+        Str newModule;
+        newModule.Format("%s%s", path, name);
+        Syn_Printf("Found '%s'\n", newModule.GetBuffer());      
+        EnumerateInterfaces(newModule);
+      }
+
+      g_dir_close(dir);
+    }
+  }
+  return true;
+}
+
+#if defined(_WIN32)  
+#define FORMAT_BUFSIZE 2048
+const char* CSynapseServer::FormatGetLastError()
+{
+  static char buf[FORMAT_BUFSIZE];
+  FormatMessage(
+    FORMAT_MESSAGE_FROM_SYSTEM | 
+    FORMAT_MESSAGE_IGNORE_INSERTS,
+    NULL,
+    GetLastError(),
+    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+    buf,
+    FORMAT_BUFSIZE,
+    NULL 
+  );
+  return buf;
+}
+
+void CSynapseServer::EnumerateInterfaces(Str &soname)
+{
+  CSynapseClientSlot slot;
+  slot.mpDLL = LoadLibrary(soname.GetBuffer());
+  if (!slot.mpDLL)
+  {
+    Syn_Printf("LoadLibrary '%s' failed\n", soname.GetBuffer());
+    Syn_Printf("  GetLastError: %s", FormatGetLastError());
+    return;
+  }
+  slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)GetProcAddress(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);
+  if (!slot.mpEnumerate)
+  {
+    Syn_Printf("GetProcAddress('%s') failed\n", NAME_SYNAPSE_ENUMERATEINTERFACES);
+    Syn_Printf("  GetLastError: %s", FormatGetLastError());
+    return;
+  }
+  Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());
+  slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);
+  if (!slot.mpClient)
+  {
+    Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());
+    if (!FreeLibrary(slot.mpDLL))
+    {
+      Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());
+    }
+    return;
+  }
+  slot.mFileName = soname;
+  mClients.push_front(slot);
+}
+
+void CSynapseClientSlot::ReleaseSO()
+{
+  if (!mpDLL)
+  {
+    Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());
+    return;
+  }
+  Syn_Printf("FreeLibrary '%s'\n", mpClient->GetInfo());
+  if (!FreeLibrary(mpDLL))
+  {
+    Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());
+  }
+  mpDLL = NULL;
+}
+
+#elif defined(__linux__) || defined (__APPLE__)
+void CSynapseServer::EnumerateInterfaces(Str &soname)
+{
+  CSynapseClientSlot slot;
+  slot.mpDLL = dlopen (soname.GetBuffer(), RTLD_NOW);
+  PFN_SYNAPSE_ENUMERATEINTERFACES *pEnumerate;
+  if (!slot.mpDLL)
+  {
+    char* error;
+    if ((error = (char *)dlerror()) == NULL)
+      error = "Unknown";
+    Syn_Printf("dlopen '%s' failed\n  dlerror: '%s'\n", soname.GetBuffer(), error);
+    return;
+  }
+  slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)dlsym(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);
+  if (!slot.mpEnumerate)
+  {
+    char* error;
+    if ((error = (char *)dlerror()) == NULL)
+      error = "Unknown";
+    Syn_Printf("dlsym '%s' failed on shared object '%s'\n  dlerror: '%s'\n", NAME_SYNAPSE_ENUMERATEINTERFACES, soname.GetBuffer(), error);
+    return;
+  }
+  Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());
+  slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);
+  if (!slot.mpClient)
+  {
+    Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());
+    if (dlclose(slot.mpDLL))
+    {
+      char* error;
+      if ((error = (char *)dlerror()) == NULL)
+        error = "Unknown";
+      Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);
+    }
+    return;
+  }
+  slot.mFileName = soname;
+  mClients.push_front(slot);
+}
+
+void CSynapseClientSlot::ReleaseSO()
+{
+  if (!mpDLL)
+  {
+    Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());
+    return;
+  }
+  Syn_Printf("dlclose '%s'\n", mpClient->GetInfo());
+  if (dlclose(mpDLL))
+  {
+    char* error;
+    if ((error = (char *)dlerror()) == NULL)
+      error = "Unknown";
+    Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);
+  }
+  mpDLL = NULL;
+}
+
+#endif
+
+void CSynapseServer::EnumerateBuiltinModule(CSynapseBuiltinClient *pClient)
+{
+  CSynapseClientSlot slot;
+  pClient->EnumerateInterfaces(this);
+  slot.mpClient = pClient;
+  slot.mType = SYN_BUILTIN;
+  mClients.push_front(slot);
+}
+
+PFN_SYN_PRINTF_VA CSynapseServer::Get_Syn_Printf()
+{
+  return g_pPrintf;
+}
+
+void CSynapseServer::TryPushStack(APIDescriptor_t *pAPI)
+{
+  list<APIDescriptor_t*>::iterator iAPI;
+  for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++)
+  {
+    if ((*iAPI) == pAPI)
+    {
+      return;
+    }
+  }
+  mStack.push_front(pAPI);      
+  mbStackChanged = true;  
+}
+
+list<CSynapseClientSlot>::iterator CSynapseServer::ShutdownClient(list<CSynapseClientSlot>::iterator iSlot)
+{
+  CSynapseClientSlot *pClientSlot = &(*iSlot);
+  if (pClientSlot->mpClient->IsActive())
+  {
+    // this should not happen except during core shutdown (i.e. editor is shutting down)
+    Syn_Printf("WARNING: ShutdownClient attempted on an active module '%s'\n", pClientSlot->mpClient->GetInfo());
+  }
+  // cleanup mStack
+  int i,api_count;
+  api_count = pClientSlot->mpClient->GetAPICount();
+  for(i=0; i<api_count; i++)
+  {
+    APIDescriptor_t *pAPI = pClientSlot->mpClient->GetAPIDescriptor(i);
+    // search this API in mStack
+    list< APIDescriptor_t *>::iterator iStack = mStack.begin();
+    while(iStack != mStack.end())
+    {
+      if (*iStack == pAPI)
+        break;
+      iStack++;
+    }
+    if (iStack != mStack.end())
+    {
+      if (pAPI->mType == SYN_REQUIRE)
+      {
+        if (pAPI->mbTableInitDone)
+        {
+          // even if non active, some SYN_REQUIRE may have been filled up
+          // look for the corresponding SYN_PROVIDE and decref
+          list< APIDescriptor_t *>::iterator iStackRequire = mStack.begin();
+          APIDescriptor_t *pMatchAPI;
+          while (iStackRequire != mStack.end())
+          {
+            pMatchAPI = *iStackRequire;
+            if ( pMatchAPI->mType == SYN_PROVIDE && MatchAPI( pMatchAPI, pAPI ) )
+              break;
+            iStackRequire++;
+          }
+          if (iStackRequire != mStack.end())
+          {
+            // we have found the corresponding SYN_PROVIDE
+            pMatchAPI->mRefCount--;
+          }
+          else
+          {
+            // this is not supposed to happen at all
+            Syn_Printf("ERROR: couldn't find the SYN_PROVIDE for an initialized SYN_REQUIRE API '%s' '%s' '%s'\n", pAPI->major_name, pAPI->minor_name, pClientSlot->mpClient->GetInfo());
+          }
+        }
+      }      
+      else if (pAPI->mType == SYN_PROVIDE)
+      {
+        // this should never happen on non active clients, it may happen during a core shutdown though
+        // if the mRefCount is != 0, that means there is at least a function table out there that will segfault things
+        Syn_Printf("WARNING: found a SYN_PROVIDE API '%s' '%s' with refcount %d in CSynapseServer::ShutdownClient for '%s'\n", pAPI->major_name, pAPI->minor_name, pAPI->mRefCount, pClientSlot->mpClient->GetInfo());
+      }
+      // mostly safe to remove it now
+      mStack.erase(iStack);
+    }
+  }
+  // we can actually release the module now
+  // NOTE: do we want to have a 'final shutdown' call to the client? (not as long as we don't have a use for it)
+  if (pClientSlot->mType == SYN_SO)
+  {
+    pClientSlot->ReleaseSO();
+  }  
+  return mClients.erase(iSlot);
+}
+
+void CSynapseServer::PushRequired(CSynapseClient *pClient)
+{
+  /* walk through the standard APIs and push them in */
+  int i,max = pClient->GetAPICount();  
+  for(i=0; i<max; i++)
+  {
+    APIDescriptor_t* pAPI = pClient->GetAPIDescriptor(i);
+    if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)
+    {
+      TryPushStack(pAPI);
+    }
+  }
+  
+  /* if this client has 'List' API Manager types, walk through them for addition too */
+  max = pClient->GetManagerListCount();
+  for(i=0; i<max; i++)
+  {
+    CSynapseAPIManager *pManager = pClient->GetManagerList(i);
+    assert(pManager->GetType() == API_LIST);
+    pManager->InitializeAPIList();
+    int j;
+    for(j=0; j<pManager->GetAPICount(); j++)
+    {
+      TryPushStack(pManager->GetAPI(j));
+    }
+  }
+  
+  /* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces
+   * and let them decide which ones they might want
+   */
+  
+  max = pClient->GetManagerMatchCount();
+
+  for(i=0; i<max; i++)
+  {
+    CSynapseAPIManager *pManager = pClient->GetManagerMatch(i);
+    // start matching all known SYN_PROVIDE APIs against this manager
+    list<CSynapseClientSlot>::iterator iClientSlot;
+    for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++)
+    {
+      CSynapseClient *pScanClient = (*iClientSlot).
+      mpClient;
+      int j,jmax = pScanClient->GetAPICount();
+      for(j=0; j<jmax; j++)
+      {
+        APIDescriptor_t *pAPI = pScanClient->GetAPIDescriptor(j);
+        if (pAPI->mType == SYN_PROVIDE)
+        {
+          if (pManager->MatchAPI(pAPI->major_name, pAPI->minor_name))
+          {
+            /*! we are going to want to load this one
+               * NOTE TTimo: what if this can not be resolved in the end?
+               * if this happens, then the whole startup will fail instead
+               * or we can use SYN_REQUIRE_ANY and drop it without consequences
+               */
+            APIDescriptor_t *pPushAPI = pManager->BuildRequireAPI(pAPI);
+            TryPushStack(pPushAPI);
+          }
+        }
+      }
+    }
+  }
+}
+
+bool CSynapseServer::MatchAPI(APIDescriptor_t *p1, APIDescriptor_t *p2)
+{
+  return MatchAPI( p1->major_name, p1->minor_name, p2->major_name, p2->minor_name );
+}
+
+bool CSynapseServer::MatchAPI(const char* major1, const char* minor1, const char* major2, const char* minor2)
+{
+  if ( strcmp( major1, major2 ) ) {
+    return false;
+  }
+  // either no minor at all for this API, or matching
+  if ( ( minor1 && minor2 ) && !strcmp( minor1, minor2 ) ) {
+    return true;
+  }
+  // or one of the minors says "*" (knowing that the majors match already)
+  if ( ( minor1 && !strcmp( minor1, "*" ) ) || ( minor2 && !strcmp( minor2, "*" ) ) ) {
+    return true;
+  }
+  return false;
+}
+
+bool CSynapseServer::ResolveAPI(APIDescriptor_t* pAPI)
+{
+  //Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);  
+  // loop through active clients, search for a client providing what we are looking for
+  list<CSynapseClientSlot>::iterator iClient;
+  for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)
+  {
+    // walk through interfaces on this client for a match
+    CSynapseClient *pScanClient = (*iClient).mpClient;
+    int i,max = pScanClient->GetAPICount();
+    for(i=0; i<max; i++)
+    {
+      APIDescriptor_t *pScanClientAPI = pScanClient->GetAPIDescriptor(i);
+      if (pScanClientAPI->mType == SYN_PROVIDE)
+      {
+        if (MatchAPI(pAPI, pScanClientAPI))
+        {
+          // can this client provide APIs yet
+          // it is possible that all of it's APIs have been filled and it's not been activated yet
+          pScanClient->CheckSetActive();
+          if (pScanClient->IsActive())
+          {
+            // make sure this interface has correct size (this is our version check)
+            if (pAPI->mSize != pScanClientAPI->mSize)
+            {
+              Syn_Printf("ERROR: version mismatch for API '%s' '%s' found in '%s' (size %d != %d)\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI->mSize, pScanClientAPI->mSize);
+              Syn_Printf("  the module and the server are incompatible\n");
+              // keep going to other APIs
+              continue;
+            }
+            // this is an active client, we can request
+            #ifdef SYNAPSE_VERBOSE
+              Syn_Printf("RequestAPI '%s' '%s' from '%s' for API %p\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI);
+            #endif
+            if (!pScanClient->RequestAPI(pAPI))
+            {
+              // this should never happen, means we think this module provides the API, but it answers that it doesn't
+              Syn_Printf("ERROR: RequestAPI failed\n");
+              return false;
+            }
+            pScanClientAPI->mRefCount++;
+            pAPI->mbTableInitDone = true;
+            return true; // job done
+          }
+          else
+          {
+            // this client is not active yet, some of it's required interfaces are not filled in
+            PushRequired(pScanClient);
+            // we will exit the scan through the APIDescriptor of this client and look at other clients
+            break;
+          }
+        }
+      }
+    }  
+  }
+  return false;
+}
+
+bool CSynapseServer::DoResolve(CSynapseClient *pClient)
+{
+  list<CSynapseClientSlot>::iterator iSlot;
+  for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)
+  {
+    if ((*iSlot).mpClient == pClient)
+      break;
+  }
+  if (iSlot == mClients.end())
+  {
+    Syn_Printf("CSynapserServer::Resolve adding new client slot '%s'\n", pClient->GetInfo());
+    CSynapseClientSlot slot;
+    slot.mpClient = pClient;
+    slot.mFileName = "local client";
+    // make it active so we can request the interfaces already
+    pClient->ForceSetActive();
+    mClients.push_front(slot);
+  }
+  else
+  {
+    // make it active so we can request the interfaces already
+    (*iSlot).mpClient->ForceSetActive();
+  }
+
+  // push the interfaces that need to be resolved for this client  
+  // NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces
+  PushRequired(pClient);
+  // start resolving now
+  // working till the stack is emptied or till we reach a dead end situation
+  // we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions
+  list<APIDescriptor_t*>::iterator iCurrent;
+  mbStackChanged = true; // init to true so we try the first elem
+  while (!mStack.empty())
+  {
+    //DumpStack();
+    if (!mbStackChanged)
+    {
+      // the stack didn't change last loop 
+      iCurrent++;
+      if (iCurrent==mStack.end())
+      {
+        Syn_Printf("ERROR: CSynapseServer::Resolve, failed to resolve\n");
+        DumpStack();
+        return false;
+      }
+      if (ResolveAPI(*iCurrent))
+      {
+        iCurrent = mStack.erase(iCurrent);
+        mbStackChanged = true;
+      }
+    }
+    else
+    {
+      // the stack changed at last loop
+      mbStackChanged = false;
+      iCurrent = mStack.begin();
+      if (ResolveAPI(*iCurrent))
+      {
+        iCurrent = mStack.erase(iCurrent);
+        mbStackChanged = true;
+      }
+    }
+  }
+  return true;
+}
+
+bool CSynapseServer::Resolve(CSynapseClient *pClient)
+{
+  bool ret = DoResolve(pClient);
+  list<CSynapseClientSlot>::iterator iClient;  
+  iClient = mClients.begin();
+  while(iClient != mClients.end())
+  {
+    CSynapseClient *pClient = (*iClient).mpClient;
+    if (!pClient->IsActive())
+    {
+      Syn_Printf("Unloading an unused module: '%s'\n", pClient->GetInfo());
+      iClient = ShutdownClient(iClient);
+    }
+    else
+      iClient++;
+  }
+  return ret;
+}
+
+void CSynapseServer::Shutdown()
+{
+  Syn_Printf("Synapse server core is shutting down\n");
+  // do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)
+  // we seperate the client shutdown calls from the dlclose cause that part is a clean decref / free situation whereas dlclose will break links without advice
+  list<CSynapseClientSlot>::iterator iClient;  
+  iClient = mClients.begin();
+  for(iClient = mClients.begin(); iClient != mClients.end(); iClient++)
+  {
+    (*iClient).mpClient->Shutdown();
+  }  
+  // now release them from the server's point of view
+  iClient = mClients.begin();
+  while(iClient != mClients.end())
+  {
+    iClient = ShutdownClient(iClient);
+  }  
+}
+
+void CSynapseServer::DumpStack()
+{
+  list<APIDescriptor_t*>::iterator iCurrent;
+  for(iCurrent=mStack.begin(); iCurrent != mStack.end(); iCurrent++)
+  {
+    APIDescriptor_t*pAPI = *iCurrent;
+    Syn_Printf("interface %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);
+  }
+}
+
+void CSynapseServer::DumpActiveClients()
+{
+  list<CSynapseClientSlot>::iterator iClient;  
+  for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)
+  {
+    CSynapseClient *pClient = (*iClient).mpClient;
+    Syn_Printf("%s", pClient->GetInfo());
+    if (pClient->IsActive())
+      Syn_Printf("\n");
+    else
+      Syn_Printf(" (not active)\n");
+  }
+}
+
+bool CSynapseServer::SelectClientConfig(const char *client_name)
+{
+  if (!mpDoc)
+    return false;
+  xmlNodePtr pNode = xmlDocGetRootElement(mpDoc);
+  if (!pNode)
+    return false;
+  // look for the client
+  pNode=pNode->children;
+  while(pNode)
+  {
+    if (pNode->type == XML_ELEMENT_NODE)
+    {
+      xmlChar *prop = xmlGetProp(pNode, (const xmlChar *)"name");
+      if (!strcmp((const char *)prop, client_name))
+      {
+        xmlFree(prop);
+        break;
+      }
+      xmlFree(prop);
+    }
+    pNode = pNode->next;
+  }
+  if (!pNode)
+    return false; // config you asked for isn't there
+  // focus
+  mpFocusedNode = pNode->children;
+  mpCurrentClientConfig = pNode;
+  return true;
+}
+
+bool CSynapseServer::GetNextConfig(char **api_name, char **minor)
+{
+  while(mpFocusedNode && mpFocusedNode->name)
+  {
+    if (mpFocusedNode->type == XML_ELEMENT_NODE && !strcmp((const char *)mpFocusedNode->name, "api"))
+    {
+      if (m_api_name)
+        xmlFree(m_api_name);
+      m_api_name = xmlGetProp(mpFocusedNode, (const xmlChar *)"name");
+      *api_name = (char *)m_api_name;
+      if (m_content)
+        g_free(m_content);
+      m_content = g_strdup((const gchar *)mpFocusedNode->children->content);
+      g_strstrip(m_content);
+      *minor = m_content;
+      mpFocusedNode = mpFocusedNode->next;
+      return true;
+    }
+    mpFocusedNode = mpFocusedNode->next;    
+  }
+  return false;
+}
+
+bool CSynapseServer::GetConfigForAPI( const char *api, char **minor ) {
+  xmlNodePtr pNode = mpCurrentClientConfig->children;
+  while ( pNode && pNode->name ) {
+    if ( pNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)pNode->name, "api" ) ) {
+      if ( m_api_name ) {
+        xmlFree( m_api_name );
+      }
+      m_api_name = xmlGetProp( pNode, (const xmlChar *)"name" );
+      if ( !strcmp( (const char *)m_api_name, api ) ) {
+        if ( m_content ) {
+          g_free( m_content );
+        }
+        m_content = g_strdup( (const gchar *)pNode->children->content );
+        g_strstrip( m_content );
+        *minor = m_content;
+        return true;
+      }
+    }
+    pNode = pNode->next;
+  }
+  return false;
+}
+
+const char *CSynapseServer::GetModuleFilename(CSynapseClient *pClient)
+{
+  list<CSynapseClientSlot>::iterator iSlot;
+  for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)
+  {
+    if ((*iSlot).mpClient == pClient)
+    {
+      if ((*iSlot).mType == SYN_BUILTIN)
+      {
+        return "";     // FIXME
+      }
+      else
+      {
+        return (*iSlot).mFileName;
+      }
+    }
+  }
+  return NULL;
+}
+
+/*
+=======================================================================
+client
+=======================================================================
+*/
+
+CSynapseClient::CSynapseClient()
+{
+}
+
+void CSynapseClient::Shutdown()
+{
+  vector<APIDescriptor_t *>::iterator iAPI;
+  for(iAPI=mAPIDescriptors.begin(); iAPI!=mAPIDescriptors.end(); iAPI++)
+  {
+    APIDescriptor_t *pAPI = *iAPI;
+    if (pAPI->mRefCount != 0)
+      Syn_Printf("WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s'\n", GetInfo(), pAPI->major_name, pAPI->minor_name);
+    else
+      delete pAPI;
+    *iAPI=NULL;
+  }
+  mAPIDescriptors.clear();
+  vector<CSynapseAPIManager *>::iterator iManager;
+  for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)
+  {
+    CSynapseAPIManager *pManager = *iManager;
+    pManager->DecRef();
+    *iManager = NULL;
+  }
+  mManagersList.clear();
+  for(iManager=mManagersMatch.begin(); iManager!=mManagersMatch.end(); iManager++)
+  {
+    CSynapseAPIManager *pManager = *iManager;
+    pManager->DecRef();
+    *iManager = NULL;
+  }
+  mManagersMatch.clear();
+}
+
+CSynapseClient::~CSynapseClient()
+{
+  // this should not be doing anything when called from here if everything went right
+  // otherwise it's likely to crash .. at least that's the sign we missed something
+  Shutdown();
+}
+
+int CSynapseClient::GetAPICount()
+{
+  return mAPIDescriptors.size();
+}
+
+APIDescriptor_t* CSynapseClient::GetAPIDescriptor(int i)
+{
+  return mAPIDescriptors[i];
+}
+
+int CSynapseClient::GetManagerMatchCount()
+{
+  return mManagersMatch.size();
+}
+
+CSynapseAPIManager* CSynapseClient::GetManagerMatch(int i)
+{
+  return mManagersMatch[i];
+}
+
+int CSynapseClient::GetManagerListCount()
+{
+  return mManagersList.size();
+}
+
+CSynapseAPIManager* CSynapseClient::GetManagerList(int i)
+{
+  return mManagersList[i];
+}
+
+bool CSynapseClient::AddAPI(const char *major, const char *minor, int size, EAPIType type, void *pTable)
+{
+  // do some safe checks before actual addition
+  if (type == SYN_REQUIRE && !pTable)
+  {
+    Syn_Printf("ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer\n", major, minor, GetInfo());
+    return false;
+  }
+  if (pTable)
+  {
+    int *pi = (int *)pTable;
+    if (pi == 0)
+    {
+      Syn_Printf("ERROR: forgot to init function table size for interface '%s' '%s' from '%s'?\n", major, minor, GetInfo());
+      return false;
+    }
+  }
+  APIDescriptor_t *pAPI = new APIDescriptor_t;
+  memset(pAPI, 0, sizeof(APIDescriptor_t));
+  strncpy(pAPI->major_name, major, MAX_APINAME);
+  if (minor)
+    strncpy(pAPI->minor_name, minor, MAX_APINAME);
+  pAPI->mType = type;
+  pAPI->mpTable = pTable;
+  // store the interface size
+  if (type == SYN_PROVIDE)
+  {
+    if (size == 0)
+    {
+      Syn_Printf("ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s')\n", major, minor, GetInfo());
+      delete pAPI;
+      return false;
+    }
+    pAPI->mSize = size;
+  }
+  else if (type == SYN_REQUIRE)
+  {    
+    if (size != 0)
+    {
+      // if a non-zero value is given in function call, use this instead of the val in table
+      *((int *)pAPI->mpTable) = size;
+      pAPI->mSize = size;
+    }
+    else
+    {
+      pAPI->mSize = *((int *)pAPI->mpTable);
+      if (pAPI->mSize == 0)
+      {
+        Syn_Printf("ERROR: didn't get an interface size ('%s' '%s' from '%s')\n", major, minor, GetInfo());
+        delete pAPI;
+        return false;
+      }
+    }
+  }
+  else
+  {
+    Syn_Printf("ERROR: AddAPI type '%d' not supported\n", type);
+    return false;
+  }
+  mAPIDescriptors.push_back(pAPI);  
+  #ifdef SYNAPSE_VERBOSE
+    Syn_Printf("AddAPI: %s %p '%s' '%s' from '%s', size %d\n", APITypeName[pAPI->mType], pAPI, major, minor, GetInfo(), pAPI->mSize);
+  #endif
+  return true;
+}
+
+#include "version.h"
+
+const char* CSynapseClient::GetInfo()
+{
+  return "CSynapseClient built " __DATE__ " " RADIANT_VERSION;
+}
+
+bool CSynapseClient::CheckSetActive()
+{
+  if (mbActive)
+    return true;
+  int i,max=GetAPICount();
+  for(i=0; i<max; i++)
+  {
+    APIDescriptor_t *pAPI = GetAPIDescriptor(i);
+    if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)
+      return false;
+  }
+  // if we have managers with fixed list, those need to be completely filled in too
+  vector<CSynapseAPIManager *>::iterator iManager;
+  for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)
+  {
+    if (!(*iManager)->CheckSetActive())
+      return false; // one of the managers doesn't have all it needs yet
+  }
+  // call OnActivate to let the client perform last minute checks
+  // NOTE: this should be fatal instead of letting the engine try other combinations
+  if (!OnActivate()) {
+    return false;
+  }
+  // yes, all required interfaces have been initialized
+  Syn_Printf("'%s' activated\n", GetInfo());
+  mbActive = true;
+  return true;
+}
+
+bool CSynapseClient::ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] ) {
+  
+  if ( !client_name ) {
+    client_name = GetName();
+  }
+  
+  Syn_Printf("Dynamic APIs for client '%s'\n", GetInfo());
+  if ( !pServer->SelectClientConfig( client_name ) )
+  {
+    Syn_Printf( "Failed to select synapse client config '%s'\n", client_name );
+    return false;
+  }
+  
+  int i = 0;
+  while ( entries[i].type != SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty
+    char *minor;
+    if ( !pServer->GetConfigForAPI( entries[i].api, &minor ) ) {
+      Syn_Printf( "GetConfigForAPI '%s' failed - invalid XML config file?\n", entries[i].api );
+      return false;
+    }
+    AddAPI( entries[i].api, minor, entries[i].size, entries[i].type, entries[i].pTable );
+    i++;
+  }
+  Syn_Printf( "%d dynamic interfaces parsed for '%s'\n", i, client_name );
+  return true;
+}
+
+void CSynapseClient::AddManager(CSynapseAPIManager *pManager)
+{
+  pManager->IncRef();
+  if (pManager->GetType() == API_LIST)
+    mManagersList.push_back(pManager);
+  else
+    mManagersMatch.push_back(pManager);
+}
+
+CSynapseAPIManager::~CSynapseAPIManager()
+{
+  vector<APIDescriptor_t *>::iterator iAPI;
+  for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)
+  {
+    APIDescriptor_t *pAPI = *iAPI;
+    if (pAPI->mRefCount != 0)
+    {
+      Syn_Printf("WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s'\n", pAPI->major_name, pAPI->minor_name);
+    }
+    delete pAPI;
+    *iAPI = NULL;
+  }
+}
+
+APIDescriptor_t* CSynapseAPIManager::PrepareRequireAPI(APIDescriptor_t *pAPI)
+{
+#ifdef _DEBUG
+  if (pAPI->mType != SYN_PROVIDE)
+  {
+    Syn_Printf("ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI\n");
+    return NULL;
+  }
+#endif
+  APIDescriptor_t *pRequireAPI = new APIDescriptor_t;
+  memcpy(pRequireAPI, pAPI, sizeof(APIDescriptor_t));
+  pRequireAPI->mType = SYN_REQUIRE_ANY;
+  pRequireAPI->mpTable = NULL;
+  pRequireAPI->mbTableInitDone = false;
+  pRequireAPI->mSize = 0; // this will have to be set correctly by the child for version checking
+  pRequireAPI->mRefCount = 0;
+  return pRequireAPI;
+}
+
+void CSynapseAPIManager::SetMatchAPI(const char *major, const char *minor)
+{
+  if (strlen(minor)>MAX_PATTERN_STRING)
+  {
+    Syn_Printf("ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s'\n", minor);
+    return;
+  }
+  strcpy(major_pattern, major);
+  strcpy(minor_pattern, minor);
+  if (strcmp(minor, "*"))
+  {
+    mType = API_LIST;
+  }
+}
+
+bool CSynapseAPIManager::MatchAPI(const char *major, const char *minor)
+{
+  assert(mType == API_MATCH);
+  
+  /*!
+  if this interface has been allocated already, avoid requesting it again..
+  */
+  vector<APIDescriptor_t *>::iterator iAPI;
+  for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)
+  {
+    if (CSynapseServer::MatchAPI((*iAPI)->major_name, (*iAPI)->minor_name, major, minor))
+      return false;
+  }
+  
+  if (!strcmp(major, major_pattern))
+    return true;
+  return false;
+}
+
+bool CSynapseAPIManager::CheckSetActive()
+{
+  if (mType == API_MATCH)
+    return false;
+  // mType == API_LIST
+  int i,max = GetAPICount();
+  for(i=0; i<max; i++)
+  {
+    if (!GetAPI(i)->mbTableInitDone)
+      return false;
+  }
+  return true;
+}
+
+void CSynapseAPIManager::InitializeAPIList()
+{
+  char minor_tok[MAX_PATTERN_STRING];
+  char *token;
+  
+  if (mAPIs.size())
+  {
+    Syn_Printf("WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager\n");
+    return;
+  }
+  
+  strncpy(minor_tok, minor_pattern, MAX_PATTERN_STRING);
+  token = strtok(minor_tok, " ");
+  while (token)
+  {
+    /* ask the child to build from scratch */
+    APIDescriptor_t *pAPI = new APIDescriptor_t;
+    memset(pAPI, 0, sizeof(APIDescriptor_t));
+    strncpy(pAPI->major_name, major_pattern, MAX_APINAME);
+    strncpy(pAPI->minor_name, token, MAX_APINAME);
+    pAPI->mType = SYN_REQUIRE_ANY;
+    FillAPITable(pAPI);
+    mAPIs.push_back(pAPI);
+    token = strtok(NULL, " ");
+  }
+}
+  
+int CSynapseAPIManager::GetAPICount()
+{
+  return mAPIs.size();
+}
+
+APIDescriptor_t* CSynapseAPIManager::GetAPI(int i)
+{
+  return mAPIs[i];
+}
+
+// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=879
+void fini_stub() {
+  printf( "fini_stub\n" );
+}