]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/eclassfgd/plugin.cpp
more eol-style
[xonotic/netradiant.git] / plugins / eclassfgd / plugin.cpp
index b5b1a50dd33e78ed45e978cf5eb5d0f5dcedc372..035df460b82ada7c60edfad51faec5b32c091cc7 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
-//#define FGD_VERBOSE // define this for extra info in the log.\r
-\r
-#include "plugin.h"\r
-\r
-/*! \file plugin.cpp\r
-    \brief .fgd entity description format\r
-\r
-    FGD loading code by Dominic Clifton - Hydra (Hydra@Hydras-World.com)\r
-\r
-    Overview\r
-    ========\r
-\r
-    This module loads .fgd files, fgd files are split into classes:\r
-\r
-      base classes\r
-      point classes (aka fixed size entities)\r
-      solid classes (aka brush entity)\r
-\r
-    This program first scans each file, building up a list structures\r
-    in memory that contain the information for all the classes found\r
-    in the file.\r
-\r
-    Then the program looks at each of the non-base classes in the list\r
-    and build the eclass_t structure from each one.\r
-\r
-    Classes can request information in other classes.\r
-\r
-    Solid/Base and Point/Base classes can have the same names as other \r
-    classes but there can be only one of each solid/point/base class with\r
-    the same name,\r
-\r
-    e.g.:  \r
-\r
-    this is NOT allowed:\r
-\r
-      @solidclass = "a"\r
-      @solidclass = "a"\r
-\r
-    this is NOT allowed:\r
-\r
-      @pointclass = "a"\r
-      @solidclass = "a"\r
-\r
-    this IS allowed:\r
-\r
-      @solidclass = "a"\r
-      @baseclass = "a"\r
-\r
-    Version History\r
-    ===============\r
-\r
-    v0.1 - 13/March/2002\r
-      - Initial version.\r
-\r
-    v0.2 - 16/March/2002\r
-      - sets e->skinpath when it finds an iconsprite(<filename>) token.\r
-\r
-    v0.3 - 21/March/2002\r
-      - Core now supports > 8 spawnflags, changes reflected here too.\r
-      - FIXED: mins/maxs were backwards when only w,h,d were in the FGD\r
-        (as opposed to the actual mins/maxs being in the .def file)\r
-      - Made sure all PointClass entities were fixed size entities\r
-        and gave them a default bounding box size if a size() setting\r
-        was not in the FGD file.\r
-      - Removed the string check for classes requesting another class\r
-        with the same name, adjusted Find_Class() so that it can search\r
-        for baseclasses only,  this fixes the problem with PointClass "light"\r
-        requesting the BaseClass "light".\r
\r
-    v0.4 - 25/March/2002\r
-      - bleh, It turns out that non-baseclasses can request non-baseclasses\r
-        so now I've changed Find_Class() so that it can ignore a specific class\r
-        (i.e. the one that's asking for others, so that classes can't request\r
-        themselves but they can request other classes of any kind with the\r
-        same name).\r
-      - made all spawnflag comments appear in one place, rather than being scattered\r
-        all over the comments if a requested class also had some spawnflags\r
-\r
-    v0.5 - 6/April/2002\r
-      - not using skinpath for sprites anymore, apprently we can code a model\r
-        module to display sprites and model files.\r
-      - model() tags are now supported.\r
-\r
-    ToDo\r
-    ====\r
-\r
-    * add support for setting the eclass_t's modelpath.\r
-      (not useful for CS, but very useful for HL).\r
-\r
-    * need to implement usage for e->skinpath in the core.\r
-\r
-    * cleanup some areas now that GetTokenExtra() is available\r
-      (some parts were written prior to it's creation).\r
-\r
-    * Import the comments between each BaseClass's main [ ] set.\r
-      (unfortunatly they're // cstyle comments, which GetToken skips over)\r
-      But still ignore comments OUTSIDE the main [ ] set.\r
-\r
-*/\r
-\r
-_QERScripLibTable g_ScripLibTable;\r
-_EClassManagerTable g_EClassManagerTable;\r
-_QERFuncTable_1 g_FuncTable;\r
-_QERFileSystemTable g_FileSystemTable;\r
-\r
-// forward declare\r
-void Eclass_ScanFile (char *filename);\r
-\r
-const char* EClass_GetExtension()\r
-{\r
-  return "fgd";\r
-}\r
-\r
-CSynapseServer* g_pSynapseServer = NULL;\r
-CSynapseClientFGD g_SynapseClient;\r
-\r
-extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces (const char *version, CSynapseServer *pServer)\r
-{\r
-  if (strcmp(version, SYNAPSE_VERSION))\r
-  {\r
-    Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version);\r
-    return NULL;\r
-  }\r
-  g_pSynapseServer = pServer;\r
-  g_pSynapseServer->IncRef();\r
-  Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());\r
-  \r
-  g_SynapseClient.AddAPI(ECLASS_MAJOR, "fgd", sizeof(_EClassTable));\r
-  g_SynapseClient.AddAPI(SCRIPLIB_MAJOR, NULL, sizeof(g_ScripLibTable), SYN_REQUIRE, &g_ScripLibTable);\r
-  g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);\r
-  g_SynapseClient.AddAPI(ECLASSMANAGER_MAJOR, NULL, sizeof(g_EClassManagerTable), SYN_REQUIRE, &g_EClassManagerTable);\r
-  \r
-  // Needs a 'default' option for this minor because we certainly don't load anything from wad files :)\r
-  g_SynapseClient.AddAPI(VFS_MAJOR, "wad", sizeof(g_FileSystemTable), SYN_REQUIRE, &g_FileSystemTable);\r
-\r
-  return &g_SynapseClient;\r
-}\r
-\r
-bool CSynapseClientFGD::RequestAPI(APIDescriptor_t *pAPI)\r
-{\r
-  if (!strcmp(pAPI->major_name, ECLASS_MAJOR))\r
-  {\r
-    _EClassTable* pTable= static_cast<_EClassTable*>(pAPI->mpTable);\r
-\r
-    pTable->m_pfnGetExtension = &EClass_GetExtension;\r
-    pTable->m_pfnScanFile = &Eclass_ScanFile;\r
-    \r
-    return true;    \r
-  }\r
-  \r
-  Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());\r
-  return false;\r
-}\r
-\r
-#include "version.h"\r
-\r
-const char* CSynapseClientFGD::GetInfo()\r
-{\r
-  return ".fgd eclass module built " __DATE__ " " RADIANT_VERSION;\r
-}\r
-\r
-// ------------------------------------------------------------------------------------------------\r
-\r
-#define CLASS_NOCLASS     0\r
-#define CLASS_BASECLASS   1\r
-#define CLASS_POINTCLASS  2\r
-#define CLASS_SOLIDCLASS  3\r
-\r
-char *classnames[] = {"NOT DEFINED","BaseClass","PointClass","SolidClass"};\r
-\r
-#define OPTION_NOOPTION   0\r
-#define OPTION_STRING     1\r
-#define OPTION_CHOICES    2\r
-#define OPTION_INTEGER    3\r
-#define OPTION_FLAGS      4\r
-\r
-char *optionnames[] = {"NOT DEFINED","String","Choices","Integer","Flags"};\r
-\r
-typedef struct choice_s {\r
-  int value;\r
-  char *name;\r
-} choice_t;\r
-\r
-typedef struct option_s {\r
-  int optiontype;\r
-  char *optioninfo;\r
-  char *epairname;\r
-  char *optiondefault;\r
-  GSList *choices; // list of choices_t\r
-} option_t;\r
-\r
-typedef struct class_s {\r
-  int classtype; // see CLASS_* above.\r
-  char *classname;\r
-  GSList *l_baselist; // when building the eclass_t, other class_s's with these names are required.\r
-  char *description;\r
-\r
-  GSList *l_optionlist; // when building the eclass_t, other class_s's with these names are required.\r
-\r
-  bool gotsize; // if set then boundingbox is valid.\r
-  vec3_t boundingbox[2]; // mins, maxs\r
-       \r
-  bool gotcolor; // if set then color is valid.  \r
-  vec3_t color; // R,G,B, loaded as 0-255 \r
-       \r
-  char *model; // relative path + filename to a model (.spr/.mdl) file, or NULL\r
-} class_t;\r
-\r
-/*\r
-===========================================================\r
-utility functions\r
-\r
-===========================================================\r
-*/\r
-char *strlower (char *start)\r
-{\r
-       char    *in;\r
-       in = start;\r
-       while (*in)\r
-       {\r
-               *in = tolower(*in); \r
-               in++;\r
-       }\r
-       return start;\r
-}\r
-\r
-char *addstr(char *dest,char *source)\r
-{\r
-  if (dest)\r
-               {\r
-    char *ptr;\r
-    int len = strlen(dest);\r
-    ptr = (char *) malloc (len + strlen(source) + 1);\r
-    strcpy(ptr,dest);\r
-    strcpy(ptr+len,source);\r
-    free(dest);\r
-    dest = ptr;\r
-  }\r
-  else\r
-                       {\r
-    dest = strdup(source);\r
-                       }\r
-  return(dest);\r
-}\r
-\r
-int getindex(unsigned int a)\r
-{\r
-  unsigned int count = 0;\r
-  unsigned int b = 0;\r
-  for (;b != a;count++)\r
-  {\r
-    b = (1<<count);\r
-    if (count > a)\r
-      return -1;    \r
-  }\r
-  return (count);\r
-}\r
-\r
-void ClearGSList (GSList* lst)\r
-{\r
-  GSList *p = lst;\r
-  while (p)\r
-  {\r
-    free (p->data);\r
-    p = g_slist_remove (p, p->data);\r
-               }\r
-}\r
-       \r
-/*!\r
-free a choice_t structure and it's contents\r
-*/\r
-void Free_Choice(choice_t *p)\r
-{\r
-  if (p->name) free(p->name);\r
-  free (p);\r
-       \r
-}\r
-\r
-/*\r
-===========================================================\r
-Main functions\r
-\r
-===========================================================\r
-*/\r
-\r
-/*!\r
-free an option_t structure and it's contents\r
-*/\r
-void Free_Option(option_t *p)\r
-{\r
-  if (p->epairname) free(p->epairname);\r
-  if (p->optiondefault) free(p->optiondefault);\r
-  if (p->optioninfo) free(p->optioninfo);\r
-  GSList *l = p->choices;\r
-  while (l)\r
-       {\r
-    Free_Choice ((choice_t *)l->data);\r
-    l = g_slist_remove (l, l->data);\r
-       }\r
-  free (p);\r
-}\r
-\r
-/*!\r
-free a class_t structure and it's contents\r
-*/\r
-void Free_Class(class_t *p)\r
-{\r
-  GSList *l = p->l_optionlist;\r
-  while (l)\r
-  {\r
-    Free_Option ((option_t *)l->data);\r
-    l = g_slist_remove (l, l->data);\r
-       }\r
-       \r
-  if (p->classname) free(p->classname);\r
-  free (p);\r
-}\r
-\r
-/*!\r
-find a class in the list\r
-*/\r
-class_t *Find_Class(GSList *l,char *classname, class_t *ignore)\r
-{\r
-  for (GSList *clst = l; clst != NULL; clst = clst->next)\r
-               {\r
-    class_t *c = (class_t *)clst->data;\r
-\r
-    if (c == ignore)\r
-      continue;\r
-\r
-    // NOTE: to speed up we could make all the classnames lower-case when they're initialised.\r
-    if (!stricmp(c->classname,classname))\r
-                       {\r
-      return c;\r
-                       }\r
-      \r
-               }\r
-  return NULL;\r
-}\r
-\r
-/*!\r
-Import as much as possible from a class_t into an eclass_t\r
-Note: this is somewhat recursive, as a class can require a class that requires a class and so on..\r
-*/\r
-void EClass_ImportFromClass(eclass_t *e, GSList *l_classes, class_t *bc)\r
-{\r
-  char color[128];\r
-\r
-  // We allocate 16k here, but only the memory actually used is kept allocated.\r
-  // this is just used for building the final comments string.\r
-  // Note: if the FGD file contains comments that are >16k (per entity) then\r
-  // radiant will crash upon loading such a file as the eclass_t will become\r
-  // corrupted.\r
-  // FIXME: we could add some length checking when building "newcomments", but\r
-  // that'd slow it down a bit.\r
-  char newcomments[16384] = "";\r
-\r
-  //Note: we override the values already in e.\r
-  //and we do it in such a way that the items that appear last in the l_baselist\r
-  //represent the final values.\r
-  \r
-  if (bc->description)\r
-       {\r
-    sprintf(newcomments,"%s\n",bc->description);\r
-    e->comments = addstr(e->comments,newcomments);\r
-    newcomments[0] = 0; // so we don't add them twice.\r
-       } \r
-\r
-\r
-  // import from other classes if required.\r
-\r
-  if (bc->l_baselist)\r
-  {\r
-    // this class requires other base classes.\r
-       \r
-    for (GSList *bclst = bc->l_baselist; bclst != NULL; bclst = bclst->next)\r
-    {      \r
-      char *requestedclass = (char *)bclst->data;\r
-\r
-//      class_t *rbc = Find_Class(l_classes, requestedclass, true);\r
-      class_t *rbc = Find_Class(l_classes, requestedclass, bc);\r
-\r
-      // make sure we don't request ourself!\r
-      if (rbc == bc)\r
-      {\r
-        Sys_Printf ("WARNING: baseclass '%s' tried to request itself!\n", bclst->data);\r
-      }\r
-                 else\r
-      {\r
-        if (!rbc)\r
-        {\r
-          Sys_Printf ("WARNING: could not find the requested baseclass '%s' when building '%s'\n", requestedclass,bc->classname);\r
-       }\r
-       else\r
-        {\r
-          // call ourself!\r
-          EClass_ImportFromClass(e, l_classes, rbc);\r
-        }\r
-       }\r
-    }\r
-  }\r
-  // SIZE\r
-  if (bc->gotsize)\r
-  {\r
-    e->fixedsize = true;\r
-    memcpy(e->mins,bc->boundingbox[0],sizeof( vec3_t ));\r
-    memcpy(e->maxs,bc->boundingbox[1],sizeof( vec3_t ));\r
-  }\r
-/*\r
-  // Hydra: apparently, this would be bad.\r
-\r
-  if (bc->sprite)\r
-  {\r
-    // Hydra - NOTE: e->skinpath is not currently used by the editor but the code\r
-    // to set it is used by both this eclass_t loader and the .DEF eclass_t loader.\r
-    // TODO: implement using e->skinpath.\r
-    if (e->skinpath)\r
-      free (e->skinpath); \r
-       \r
-    e->skinpath = strdup(bc->sprite);\r
-  }\r
-*/\r
-\r
-  // MODEL\r
-  if (bc->model)\r
-  {\r
-    if (e->modelpath)\r
-      free (e->modelpath);\r
-\r
-    e->modelpath = strdup(bc->model);\r
-  }\r
-\r
-  // COLOR\r
-  if (bc->gotcolor)\r
-  {\r
-    memcpy(e->color,bc->color,sizeof( vec3_t ));\r
-    sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);\r
-       e->texdef.SetName(color);\r
-  }\r
-\r
-  // SPAWNFLAGS and COMMENTS\r
-  if (bc->l_optionlist)\r
-  {\r
-    for (GSList *optlst = bc->l_optionlist; optlst != NULL; optlst = optlst->next)\r
-    {\r
-      option_t *opt = (option_t*) optlst->data;\r
-       \r
-      if (opt->optiontype != OPTION_FLAGS)\r
-      {\r
-        // add some info to the comments.\r
-        if (opt->optioninfo)\r
-        {\r
-          sprintf(newcomments+strlen(newcomments),"%s '%s' %s%s\n",\r
-            opt->epairname, \r
-            opt->optioninfo ? opt->optioninfo : "", \r
-            opt->optiondefault ? ", Default: " : "",\r
-            opt->optiondefault ? opt->optiondefault : "");      \r
-        }\r
-        else\r
-        {\r
-          sprintf(newcomments+strlen(newcomments),"%s %s%s\n",\r
-            opt->epairname, \r
-            opt->optiondefault ? ", Default: " : "",\r
-            opt->optiondefault ? opt->optiondefault : "");      \r
-        }\r
-      }\r
-\r
-      GSList *choicelst;\r
-      switch(opt->optiontype)\r
-      {        \r
-        case OPTION_FLAGS :\r
-          // grab the flags.\r
-          for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)\r
-          {\r
-            choice_t *choice = (choice_t*) choicelst->data;\r
-       \r
-            int index = getindex(choice->value);\r
-            index--;\r
-            if (index < MAX_FLAGS)\r
-            {\r
-              strcpy(e->flagnames[index],choice->name);\r
-            }\r
-            else\r
-                 {\r
-              Sys_Printf ("WARNING: baseclass '%s' has a spawnflag out of range, ignored!\n", bc->classname);\r
-            }\r
-          }\r
-                           break;\r
-        case OPTION_CHOICES :\r
-          strcat(newcomments,"  Choices:\n");\r
-          for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)\r
-          {\r
-            choice_t *choice = (choice_t*) choicelst->data;\r
-            sprintf(newcomments+strlen(newcomments),"  %5d - %s\n",choice->value,choice->name);\r
-          }       \r
-          break;\r
-      }\r
-    }\r
-       }\r
-\r
-  // name\r
-  if (e->name) free(e->name);\r
-  e->name = strdup(bc->classname);\r
-       \r
-  // fixed size initialisation\r
-  if (bc->classtype == CLASS_POINTCLASS)\r
-  {    \r
-    e->fixedsize = true;\r
-    // some point classes dont seem to have size()'s in the fgd, so set up a default here..\r
-    if ((e->mins[0] == 0) && (e->mins[1] == 0) && (e->mins[2] == 0) && \r
-        (e->maxs[0] == 0) && (e->maxs[1] == 0) && (e->maxs[2] == 0))\r
-  {\r
-      e->mins[0] = -8;\r
-      e->mins[1] = -8;\r
-      e->mins[2] = -8;\r
-      e->maxs[0] = 8;\r
-      e->maxs[1] = 8;\r
-      e->maxs[2] = 8;\r
-  }\r
-\r
-    if (e->texdef.GetName() == NULL)\r
-    {\r
-      // no color specified for this entity in the fgd file\r
-      // set one now\r
-      // Note: if this eclass_t is not fully built, then this may be\r
-      // overridden with the correct color.\r
-      \r
-      e->color[0] = 1;\r
-      e->color[1] = 0.5; // how about a nice bright pink, mmm, nice! :)\r
-      e->color[2] = 1;\r
-\r
-      sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);\r
-       e->texdef.SetName(color);\r
-    }\r
-  }\r
-\r
-  // COMMENTS\r
-  if (newcomments[0])\r
-  {\r
-    e->comments = addstr(e->comments,newcomments);\r
-  }\r
-}\r
-\r
-/*!\r
-create the eclass_t structures and add to the global list.\r
-*/\r
-void Create_EClasses(GSList *l_classes)\r
-{\r
-  int count = 0;\r
-  // loop through the loaded structures finding all the non BaseClasses\r
-  for (GSList *clst = l_classes; clst != NULL; clst = clst->next)\r
-  {\r
-    class_t *c = (class_t *)clst->data;\r
-\r
-    if (c->classtype == CLASS_BASECLASS) // not looking for these.\r
-      continue;\r
-\r
-    eclass_t *e = (eclass_t *) malloc( sizeof( eclass_s ));\r
-    memset(e,0,sizeof( eclass_s ));\r
-\r
-    EClass_ImportFromClass(e, l_classes, c );\r
-\r
-    // radiant will crash if this is null, and it still could be at this point.\r
-    if (!e->comments)\r
-    {\r
-      e->comments=strdup("No description available, check documentation\n");\r
-    }\r
-\r
-    // dump the spawnflags to the end of the comments.\r
-    int i;\r
-    bool havespawnflags;\r
-\r
-    havespawnflags = false;\r
-    for (i = 0 ; i < MAX_FLAGS ; i++)\r
-    {\r
-      if (*e->flagnames[i]) havespawnflags = true;\r
-    }\r
-\r
-    if (havespawnflags)\r
-    {\r
-      char spawnline[80];\r
-      e->comments = addstr(e->comments,"Spawnflags\n");\r
-      for (i = 0 ; i < MAX_FLAGS ; i++)\r
-      {\r
-        if (*e->flagnames[i]) \r
-        {\r
-          sprintf(spawnline,"  %d - %s\n", 1<<i, e->flagnames[i]); \r
-          e->comments = addstr(e->comments,spawnline);\r
-        }\r
-      }\r
-    }\r
-\r
-    Eclass_InsertAlphabetized (e);\r
-    count ++;\r
-    // Hydra: ttimo, I don't think this code is required...\r
-               // single ?\r
-               *Get_Eclass_E() = e;\r
-    Set_Eclass_Found(true);\r
-               if ( Get_Parsing_Single() )\r
-                       break;\r
-  }\r
-\r
-  Sys_Printf ("FGD Loaded %d entities.\n", count);\r
-}\r
-\r
-void Eclass_ScanFile (char *filename)\r
-{\r
-       int             size;\r
-       char    *data;\r
-       char    temp[1024];\r
-  GSList *l_classes = NULL;\r
-       char  token_debug[1024]; //++Hydra FIXME: cleanup this.\r
-  bool done = false;\r
-  int len,classtype;\r
-\r
-  char *token = Token();\r
-       \r
-       QE_ConvertDOSToUnixName( temp, filename );\r
-       \r
-       size = vfsLoadFullPathFile (filename, (void**)&data);\r
-  if (size <= 0)\r
-  {\r
-    Sys_FPrintf (SYS_ERR, "Eclass_ScanFile: %s not found\n", filename);\r
-    return;\r
-  }\r
-       Sys_Printf ("ScanFile: %s\n", temp);    \r
-\r
-  // start parsing the file\r
-  StartTokenParsing(data);\r
-\r
-  // build a list of base classes first\r
-\r
-  while (!done)\r
-               {\r
-    // find an @ sign.\r
-    do\r
-    {\r
-      if (!GetToken(true))\r
-      {\r
-        done = true;\r
-        break;\r
-      }\r
-    } while (token[0] != '@');\r
-\r
-    strcpy(temp,token+1); // skip the @\r
-\r
-    classtype = CLASS_NOCLASS;\r
-    if (!stricmp(temp,"BaseClass")) classtype = CLASS_BASECLASS;\r
-    if (!stricmp(temp,"PointClass")) classtype = CLASS_POINTCLASS;\r
-    if (!stricmp(temp,"SolidClass")) classtype = CLASS_SOLIDCLASS;\r
-\r
-    if (classtype)\r
-    {\r
-      class_t *newclass = (class_t *) malloc( sizeof(class_s) );\r
-      memset( newclass, 0, sizeof(class_s) );\r
-      newclass->classtype = classtype;\r
-\r
-      while (1)\r
-      {\r
-        GetTokenExtra(false,"(",false); // option or =\r
-        strcpy(token_debug,token);\r
-\r
-        if (!strcmp(token,"="))\r
-        {\r
-          UnGetToken();\r
-          break;\r
-        }\r
-                       else\r
-        {\r
-          strlower(token);\r
-          if (!strcmp(token,"base"))\r
-          {\r
-            GetTokenExtra(false,"(",true); // (\r
-\r
-            if (!strcmp(token,"("))\r
-            {\r
-              while (GetTokenExtra(false,",)",false)) // option) or option,\r
-              {\r
-                newclass->l_baselist = g_slist_append (newclass->l_baselist, strdup(token));\r
-\r
-                GetTokenExtra(false,",)",true); // , or )\r
-                if (!strcmp(token,")"))\r
-                  break;\r
-\r
-              }\r
-            }\r
-          }\r
-          else if (!strcmp(token,"size"))\r
-          {\r
-            // parse (w h d) or (x y z, x y z)\r
-\r
-            GetTokenExtra(false,"(",true); // (\r
-            if (!strcmp(token,"("))\r
-            {\r
-              int sizedone = false;\r
-              float w,h,d;\r
-              GetToken(false);\r
-              w = atof(token);\r
-              GetToken(false);\r
-              h = atof(token);\r
-              GetToken(false); // number) or number ,\r
-              strcpy(temp,token);\r
-              len = strlen(temp);\r
-              if (temp[len-1] == ')') sizedone = true;                \r
-              temp[len-1] = 0;\r
-              d = atof(temp);\r
-              if (sizedone)\r
-              {\r
-                // only one set of cordinates supplied, change the W,H,D to mins/maxs\r
-                newclass->boundingbox[0][0] = 0 - (w / 2);\r
-                newclass->boundingbox[1][0] = w / 2;\r
-                newclass->boundingbox[0][1] = 0 - (h / 2);\r
-                newclass->boundingbox[1][1] = h / 2;\r
-                newclass->boundingbox[0][2] = 0 - (d / 2);\r
-                newclass->boundingbox[1][2] = d / 2;\r
-                newclass->gotsize = true;\r
-              }\r
-              else\r
-              {\r
-                newclass->boundingbox[0][0] = w;\r
-                newclass->boundingbox[0][1] = h;\r
-                newclass->boundingbox[0][2] = d;\r
-                GetToken(false);\r
-                newclass->boundingbox[1][0] = atof(token);\r
-                GetToken(false);\r
-                newclass->boundingbox[1][1] = atof(token);\r
-/*\r
-                GetToken(false); // "number)" or "number )"\r
-                strcpy(temp,token);\r
-                len = strlen(temp);\r
-                if (temp[len-1] == ')') \r
-                  temp[len-1] = 0;\r
-                else\r
-                  GetToken(false); // )\r
-                newclass->boundingbox[1][2] = atof(temp);\r
-*/\r
-                GetTokenExtra(false,")",false); // number\r
-                newclass->boundingbox[1][2] = atof(token);\r
-                newclass->gotsize = true;\r
-                GetTokenExtra(false,")",true); // )\r
-              }\r
-            }\r
-          }\r
-          else if (!strcmp(token,"color"))\r
-          {\r
-            GetTokenExtra(false,"(",true); // (\r
-            if (!strcmp(token,"("))\r
-            {\r
-              // get the color values (0-255) and normalize them if required.\r
-              GetToken(false);\r
-              newclass->color[0] = atof(token);\r
-              if (newclass->color[0] > 1)\r
-                newclass->color[0]/=255;\r
-              GetToken(false);\r
-              newclass->color[1] = atof(token);\r
-              if (newclass->color[1] > 1)\r
-                newclass->color[1]/=255;\r
-              GetToken(false);\r
-              strcpy(temp,token);\r
-              len = strlen(temp);\r
-              if (temp[len-1] == ')') temp[len-1] = 0;\r
-              newclass->color[2] = atof(temp);\r
-              if (newclass->color[2] > 1)\r
-                newclass->color[2]/=255;\r
-              newclass->gotcolor = true;\r
-            }\r
-          }\r
-          else if (!strcmp(token,"iconsprite"))\r
-          {\r
-            GetTokenExtra(false,"(",true); // (\r
-            if (!strcmp(token,"("))\r
-            {\r
-              GetTokenExtra(false,")",false); // filename)\r
-              // the model plugins will handle sprites too.\r
-              // newclass->sprite = strdup(token);\r
-              newclass->model = strdup(token);\r
-              GetTokenExtra(false,")",true); // )\r
-            }\r
-          }\r
-          else if (!strcmp(token,"model"))\r
-          {\r
-            GetTokenExtra(false,"(",true); // (\r
-            if (!strcmp(token,"("))\r
-            {\r
-              GetTokenExtra(false,")",false); // filename)\r
-              newclass->model = strdup(token);\r
-              GetTokenExtra(false,")",true); // )\r
-            }\r
-          }\r
-          else \r
-          {\r
-            // Unsupported\r
-            GetToken(false); // skip it.\r
-          }\r
-\r
-        }\r
-      }\r
-\r
-      GetToken(false); // =\r
-      strcpy(token_debug,token);\r
-      if (!strcmp(token,"="))\r
-      {\r
-        GetToken(false);\r
-        newclass->classname = strdup(token);\r
-      }\r
-\r
-      // Get the description\r
-      if (newclass->classtype != CLASS_BASECLASS)\r
-      {\r
-        GetToken(false);\r
-        if (!strcmp(token,":"))\r
-        {\r
-          GetToken(false);\r
-          newclass->description = strdup(token);\r
-        } else UnGetToken(); // no description\r
-      }\r
-\r
-      // now build the option list.\r
-      GetToken(true); // [ or []\r
-\r
-      if (strcmp(token,"[]"))  // got some options ?\r
-      {\r
-        if (!strcmp(token,"["))\r
-        {\r
-          // yup\r
-          bool optioncomplete = false;\r
-          option_t *newoption;\r
-\r
-          while (1)\r
-          {\r
-            GetToken(true);\r
-            if (!strcmp(token,"]"))\r
-              break; // no more options\r
-\r
-            // parse the data and build the option_t\r
-\r
-            strcpy(temp,token);\r
-            len = strlen(temp);\r
-            char *ptr = strchr(temp,'(');\r
-\r
-            if (!ptr)\r
-                                     break;\r
-\r
-            newoption = (option_t *) malloc ( sizeof( option_s ));\r
-            memset( newoption, 0, sizeof( option_s ));\r
-\r
-            *ptr++ = 0;\r
-            newoption->epairname = strdup(temp);\r
-\r
-            len = strlen(ptr);\r
-            if (ptr[len-1] != ')')\r
-              break;\r
-\r
-            ptr[len-1] = 0;\r
-            strlower(ptr);\r
-            if (!strcmp(ptr,"integer"))\r
-            {\r
-              newoption->optiontype = OPTION_INTEGER;\r
-            }\r
-            else if (!strcmp(ptr,"choices"))\r
-            {\r
-              newoption->optiontype = OPTION_CHOICES;\r
-            }\r
-            else if (!strcmp(ptr,"flags"))\r
-            {\r
-              newoption->optiontype = OPTION_FLAGS;\r
-            }\r
-            else // string\r
-            {\r
-              newoption->optiontype = OPTION_STRING;\r
-            }\r
-\r
-            switch (newoption->optiontype)\r
-            {\r
-              case OPTION_STRING :\r
-              case OPTION_INTEGER :\r
-                if (!TokenAvailable())\r
-                {\r
-                  optioncomplete = true;\r
-                               break;\r
-               }\r
-                GetToken(false); // :\r
-                strcpy(token_debug,token);\r
-                if ((token[0] == ':') && (strlen(token) > 1))\r
-                {\r
-                  newoption->optioninfo = strdup(token+1);\r
-                }\r
-                else\r
-                {\r
-                  GetToken(false);\r
-                  newoption->optioninfo = strdup(token);\r
-                }\r
-                if (TokenAvailable()) // default value ?\r
-                {\r
-                  GetToken(false);\r
-                  if (!strcmp(token,":"))\r
-                  {\r
-                    if (GetToken(false))\r
-                    {\r
-                      newoption->optiondefault = strdup(token);\r
-                      optioncomplete = true;\r
-                    }\r
-                  }\r
-                }\r
-                else\r
-                {\r
-                  optioncomplete = true;\r
-                }\r
-                break;\r
-\r
-              case OPTION_CHOICES :\r
-                GetTokenExtra(false,":",true); // : or :"something like this" (bah!)\r
-                strcpy(token_debug,token);\r
-                if ((token[0] == ':') && (strlen(token) > 1))\r
-                {\r
-                  if (token[1] == '\"')\r
-                  {\r
-                    strcpy(temp,token+2);\r
-                    while (1)\r
-                    {\r
-                      if (!GetToken(false))\r
-                        break;\r
-                      strcat(temp," ");\r
-                      strcat(temp,token);\r
-                      len = strlen(temp);\r
-                      if (temp[len-1] == '\"')\r
-                      {\r
-                        temp[len-1] = 0;\r
-                        break;\r
-                      }\r
-                    }\r
-                  }\r
-                  newoption->optioninfo = strdup(temp);\r
-                }\r
-                else\r
-                {\r
-                  GetToken(false);\r
-                  newoption->optioninfo = strdup(token);\r
-                }\r
-                GetToken(false); // : or =\r
-                strcpy(token_debug,token);\r
-                if (!strcmp(token,":"))\r
-                {\r
-                  GetToken(false);\r
-                  newoption->optiondefault = strdup(token);\r
-                }\r
-                else\r
-                {\r
-                  UnGetToken();\r
-                }\r
-                // And Follow on...\r
-              case OPTION_FLAGS :\r
-                GetToken(false); // : or =\r
-                strcpy(token_debug,token);\r
-                if (strcmp(token,"=")) // missing ?\r
-                  break;\r
-\r
-                GetToken(true); // [\r
-                strcpy(token_debug,token);\r
-                if (strcmp(token,"[")) // missing ?\r
-                  break;\r
-\r
-                choice_t *newchoice;\r
-                while (1)\r
-                {\r
-                  GetTokenExtra(true,":",true); // "]" or "number", or "number:"\r
-                  strcpy(token_debug,token);\r
-                  if (!strcmp(token,"]")) // no more ?\r
-                  {\r
-                    optioncomplete = true;\r
-                    break;\r
-                  }\r
-                  strcpy(temp,token);\r
-                  len = strlen(temp);\r
-                  if (temp[len-1] == ':')\r
-                  {\r
-                    temp[len-1] = 0;\r
-                  }\r
-                  else\r
-                  {\r
-                    GetToken(false); // :\r
-                    if (strcmp(token,":")) // missing ?\r
-                      break;\r
-                  }\r
-                  if (!TokenAvailable())\r
-                    break;\r
-                  GetToken(false); // the name\r
-\r
-                  newchoice = (choice_t *) malloc ( sizeof( choice_s ));\r
-                  memset( newchoice, 0, sizeof( choice_s ));\r
-\r
-                  newchoice->value = atoi(temp);\r
-                  newchoice->name = strdup(token);\r
-\r
-                  newoption->choices = g_slist_append(newoption->choices, newchoice);\r
-\r
-                  // ignore any remaining tokens on the line\r
-                  while (TokenAvailable()) GetToken(false);\r
-\r
-                  // and it we found a "]" on the end of the line, put it back in the queue.\r
-                  if (!strcmp(token,"]")) UnGetToken();\r
-                }\r
-                               break;\r
-\r
-            }\r
-\r
-            // add option to the newclass\r
-\r
-            if (optioncomplete)\r
-            {\r
-              if (newoption)\r
-              {\r
-                // add it to the list.\r
-                newclass->l_optionlist = g_slist_append(newclass->l_optionlist, newoption);\r
-              }\r
-            }\r
-            else\r
-            {\r
-              Sys_Printf ("%WARNING: Parse error occured in '%s - %s'\n",classnames[newclass->classtype],newclass->classname);\r
-              Free_Option(newoption);\r
-            }\r
-\r
-          }\r
-        }\r
-        else\r
-        {\r
-          UnGetToken(); // shouldn't get here.\r
-        }\r
-      }\r
-\r
-      // add it to our list.\r
-      l_classes = g_slist_append (l_classes, newclass);\r
-\r
-    }\r
-  }\r
-\r
-  // finished with the file now.\r
-  g_free(data);\r
-\r
-  Sys_Printf ("FGD scan complete, building entities...\n");\r
-\r
-  // Once we get here we should have a few (!) lists in memory that we\r
-  // can extract all the information required to build a the eclass_t structures.\r
-\r
-  Create_EClasses(l_classes);\r
-\r
-  // Free everything\r
-\r
-  GSList *p = l_classes;\r
-  while (p)\r
-  {\r
-    class_t *tmpclass = (class_t *)p->data;\r
-\r
-#ifdef FGD_VERBOSE\r
-    // DEBUG: dump the info...\r
-    Sys_Printf ("%s: %s (", classnames[tmpclass->classtype],tmpclass->classname);\r
-    for (GSList *tmp = tmpclass->l_baselist; tmp != NULL; tmp = tmp->next)\r
-    {\r
-      if (tmp != tmpclass->l_baselist)\r
-      {\r
-        Sys_Printf (", ");\r
-      }\r
-      Sys_Printf ("%s", (char *)tmp->data);\r
-    }\r
-    if (tmpclass->gotsize)\r
-    {\r
-      sprintf(temp,"(%.0f %.0f %.0f) - (%.0f %.0f %.0f)",tmpclass->boundingbox[0][0],\r
-        tmpclass->boundingbox[0][1],\r
-        tmpclass->boundingbox[0][2],\r
-        tmpclass->boundingbox[1][0],\r
-        tmpclass->boundingbox[1][1],\r
-        tmpclass->boundingbox[1][2]);\r
-    } else strcpy(temp,"No Size");\r
-    Sys_Printf (") '%s' Size: %s",tmpclass->description ? tmpclass->description : "No description",temp);\r
-    if (tmpclass->gotcolor)\r
-    {\r
-      sprintf(temp,"(%d %d %d)",tmpclass->color[0],\r
-        tmpclass->color[1],\r
-        tmpclass->color[2]);\r
-    } else strcpy(temp,"No Color");\r
-    Sys_Printf (" Color: %s Options:\n",temp);\r
-    if (!tmpclass->l_optionlist)\r
-    {\r
-      Sys_Printf ("  No Options\n");\r
-    }\r
-    else\r
-    {\r
-      option_t *tmpoption;\r
-      int count;\r
-      GSList *olst;\r
-      for (olst = tmpclass->l_optionlist, count = 1; olst != NULL; olst = olst->next, count ++)\r
-      {\r
-        tmpoption = (option_t *)olst->data;\r
-        Sys_Printf ("  %d, Type: %s, EPair: %s\n", count,optionnames[tmpoption->optiontype], tmpoption->epairname );\r
-\r
-        choice_t *tmpchoice;\r
-        GSList *clst;\r
-        int ccount;\r
-        for (clst = tmpoption->choices, ccount = 1; clst != NULL; clst = clst->next, ccount ++)\r
-        {\r
-          tmpchoice = (choice_t *)clst->data;\r
-          Sys_Printf ("    %d, Value: %d, Name: %s\n", ccount, tmpchoice->value, tmpchoice->name);\r
-        }\r
-      }\r
-    }\r
-\r
-#endif\r
-\r
-    // free the baselist.\r
-    ClearGSList(tmpclass->l_baselist);\r
-    Free_Class (tmpclass);\r
-    p = g_slist_remove (p, p->data);\r
-               }\r
-               \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
+*/
+
+//#define FGD_VERBOSE // define this for extra info in the log.
+
+#include "plugin.h"
+
+/*! \file plugin.cpp
+    \brief .fgd entity description format
+
+    FGD loading code by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
+
+    Overview
+    ========
+
+    This module loads .fgd files, fgd files are split into classes:
+
+      base classes
+      point classes (aka fixed size entities)
+      solid classes (aka brush entity)
+
+    This program first scans each file, building up a list structures
+    in memory that contain the information for all the classes found
+    in the file.
+
+    Then the program looks at each of the non-base classes in the list
+    and build the eclass_t structure from each one.
+
+    Classes can request information in other classes.
+
+    Solid/Base and Point/Base classes can have the same names as other 
+    classes but there can be only one of each solid/point/base class with
+    the same name,
+
+    e.g.:  
+
+    this is NOT allowed:
+
+      @solidclass = "a"
+      @solidclass = "a"
+
+    this is NOT allowed:
+
+      @pointclass = "a"
+      @solidclass = "a"
+
+    this IS allowed:
+
+      @solidclass = "a"
+      @baseclass = "a"
+
+    Version History
+    ===============
+
+    v0.1 - 13/March/2002
+      - Initial version.
+
+    v0.2 - 16/March/2002
+      - sets e->skinpath when it finds an iconsprite(<filename>) token.
+
+    v0.3 - 21/March/2002
+      - Core now supports > 8 spawnflags, changes reflected here too.
+      - FIXED: mins/maxs were backwards when only w,h,d were in the FGD
+        (as opposed to the actual mins/maxs being in the .def file)
+      - Made sure all PointClass entities were fixed size entities
+        and gave them a default bounding box size if a size() setting
+        was not in the FGD file.
+      - Removed the string check for classes requesting another class
+        with the same name, adjusted Find_Class() so that it can search
+        for baseclasses only,  this fixes the problem with PointClass "light"
+        requesting the BaseClass "light".
+    v0.4 - 25/March/2002
+      - bleh, It turns out that non-baseclasses can request non-baseclasses
+        so now I've changed Find_Class() so that it can ignore a specific class
+        (i.e. the one that's asking for others, so that classes can't request
+        themselves but they can request other classes of any kind with the
+        same name).
+      - made all spawnflag comments appear in one place, rather than being scattered
+        all over the comments if a requested class also had some spawnflags
+
+    v0.5 - 6/April/2002
+      - not using skinpath for sprites anymore, apprently we can code a model
+        module to display sprites and model files.
+      - model() tags are now supported.
+
+    ToDo
+    ====
+
+    * add support for setting the eclass_t's modelpath.
+      (not useful for CS, but very useful for HL).
+
+    * need to implement usage for e->skinpath in the core.
+
+    * cleanup some areas now that GetTokenExtra() is available
+      (some parts were written prior to it's creation).
+
+    * Import the comments between each BaseClass's main [ ] set.
+      (unfortunatly they're // cstyle comments, which GetToken skips over)
+      But still ignore comments OUTSIDE the main [ ] set.
+
+*/
+
+_QERScripLibTable g_ScripLibTable;
+_EClassManagerTable g_EClassManagerTable;
+_QERFuncTable_1 g_FuncTable;
+_QERFileSystemTable g_FileSystemTable;
+
+// forward declare
+void Eclass_ScanFile (char *filename);
+
+const char* EClass_GetExtension()
+{
+  return "fgd";
+}
+
+CSynapseServer* g_pSynapseServer = NULL;
+CSynapseClientFGD g_SynapseClient;
+
+extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces (const char *version, CSynapseServer *pServer)
+{
+  if (strcmp(version, SYNAPSE_VERSION))
+  {
+    Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version);
+    return NULL;
+  }
+  g_pSynapseServer = pServer;
+  g_pSynapseServer->IncRef();
+  Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());
+  
+  g_SynapseClient.AddAPI(ECLASS_MAJOR, "fgd", sizeof(_EClassTable));
+  g_SynapseClient.AddAPI(SCRIPLIB_MAJOR, NULL, sizeof(g_ScripLibTable), SYN_REQUIRE, &g_ScripLibTable);
+  g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);
+  g_SynapseClient.AddAPI(ECLASSMANAGER_MAJOR, NULL, sizeof(g_EClassManagerTable), SYN_REQUIRE, &g_EClassManagerTable);
+  
+  // Needs a 'default' option for this minor because we certainly don't load anything from wad files :)
+  g_SynapseClient.AddAPI(VFS_MAJOR, "wad", sizeof(g_FileSystemTable), SYN_REQUIRE, &g_FileSystemTable);
+
+  return &g_SynapseClient;
+}
+
+bool CSynapseClientFGD::RequestAPI(APIDescriptor_t *pAPI)
+{
+  if (!strcmp(pAPI->major_name, ECLASS_MAJOR))
+  {
+    _EClassTable* pTable= static_cast<_EClassTable*>(pAPI->mpTable);
+
+    pTable->m_pfnGetExtension = &EClass_GetExtension;
+    pTable->m_pfnScanFile = &Eclass_ScanFile;
+    
+    return true;    
+  }
+  
+  Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
+  return false;
+}
+
+#include "version.h"
+
+const char* CSynapseClientFGD::GetInfo()
+{
+  return ".fgd eclass module built " __DATE__ " " RADIANT_VERSION;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+#define CLASS_NOCLASS     0
+#define CLASS_BASECLASS   1
+#define CLASS_POINTCLASS  2
+#define CLASS_SOLIDCLASS  3
+
+char *classnames[] = {"NOT DEFINED","BaseClass","PointClass","SolidClass"};
+
+#define OPTION_NOOPTION   0
+#define OPTION_STRING     1
+#define OPTION_CHOICES    2
+#define OPTION_INTEGER    3
+#define OPTION_FLAGS      4
+
+char *optionnames[] = {"NOT DEFINED","String","Choices","Integer","Flags"};
+
+typedef struct choice_s {
+  int value;
+  char *name;
+} choice_t;
+
+typedef struct option_s {
+  int optiontype;
+  char *optioninfo;
+  char *epairname;
+  char *optiondefault;
+  GSList *choices; // list of choices_t
+} option_t;
+
+typedef struct class_s {
+  int classtype; // see CLASS_* above.
+  char *classname;
+  GSList *l_baselist; // when building the eclass_t, other class_s's with these names are required.
+  char *description;
+
+  GSList *l_optionlist; // when building the eclass_t, other class_s's with these names are required.
+
+  bool gotsize; // if set then boundingbox is valid.
+  vec3_t boundingbox[2]; // mins, maxs
+       
+  bool gotcolor; // if set then color is valid.  
+  vec3_t color; // R,G,B, loaded as 0-255 
+       
+  char *model; // relative path + filename to a model (.spr/.mdl) file, or NULL
+} class_t;
+
+/*
+===========================================================
+utility functions
+
+===========================================================
+*/
+char *strlower (char *start)
+{
+       char    *in;
+       in = start;
+       while (*in)
+       {
+               *in = tolower(*in); 
+               in++;
+       }
+       return start;
+}
+
+char *addstr(char *dest,char *source)
+{
+  if (dest)
+               {
+    char *ptr;
+    int len = strlen(dest);
+    ptr = (char *) malloc (len + strlen(source) + 1);
+    strcpy(ptr,dest);
+    strcpy(ptr+len,source);
+    free(dest);
+    dest = ptr;
+  }
+  else
+                       {
+    dest = strdup(source);
+                       }
+  return(dest);
+}
+
+int getindex(unsigned int a)
+{
+  unsigned int count = 0;
+  unsigned int b = 0;
+  for (;b != a;count++)
+  {
+    b = (1<<count);
+    if (count > a)
+      return -1;    
+  }
+  return (count);
+}
+
+void ClearGSList (GSList* lst)
+{
+  GSList *p = lst;
+  while (p)
+  {
+    free (p->data);
+    p = g_slist_remove (p, p->data);
+               }
+}
+       
+/*!
+free a choice_t structure and it's contents
+*/
+void Free_Choice(choice_t *p)
+{
+  if (p->name) free(p->name);
+  free (p);
+       
+}
+
+/*
+===========================================================
+Main functions
+
+===========================================================
+*/
+
+/*!
+free an option_t structure and it's contents
+*/
+void Free_Option(option_t *p)
+{
+  if (p->epairname) free(p->epairname);
+  if (p->optiondefault) free(p->optiondefault);
+  if (p->optioninfo) free(p->optioninfo);
+  GSList *l = p->choices;
+  while (l)
+       {
+    Free_Choice ((choice_t *)l->data);
+    l = g_slist_remove (l, l->data);
+       }
+  free (p);
+}
+
+/*!
+free a class_t structure and it's contents
+*/
+void Free_Class(class_t *p)
+{
+  GSList *l = p->l_optionlist;
+  while (l)
+  {
+    Free_Option ((option_t *)l->data);
+    l = g_slist_remove (l, l->data);
+       }
+       
+  if (p->classname) free(p->classname);
+  free (p);
+}
+
+/*!
+find a class in the list
+*/
+class_t *Find_Class(GSList *l,char *classname, class_t *ignore)
+{
+  for (GSList *clst = l; clst != NULL; clst = clst->next)
+               {
+    class_t *c = (class_t *)clst->data;
+
+    if (c == ignore)
+      continue;
+
+    // NOTE: to speed up we could make all the classnames lower-case when they're initialised.
+    if (!stricmp(c->classname,classname))
+                       {
+      return c;
+                       }
+      
+               }
+  return NULL;
+}
+
+/*!
+Import as much as possible from a class_t into an eclass_t
+Note: this is somewhat recursive, as a class can require a class that requires a class and so on..
+*/
+void EClass_ImportFromClass(eclass_t *e, GSList *l_classes, class_t *bc)
+{
+  char color[128];
+
+  // We allocate 16k here, but only the memory actually used is kept allocated.
+  // this is just used for building the final comments string.
+  // Note: if the FGD file contains comments that are >16k (per entity) then
+  // radiant will crash upon loading such a file as the eclass_t will become
+  // corrupted.
+  // FIXME: we could add some length checking when building "newcomments", but
+  // that'd slow it down a bit.
+  char newcomments[16384] = "";
+
+  //Note: we override the values already in e.
+  //and we do it in such a way that the items that appear last in the l_baselist
+  //represent the final values.
+  
+  if (bc->description)
+       {
+    sprintf(newcomments,"%s\n",bc->description);
+    e->comments = addstr(e->comments,newcomments);
+    newcomments[0] = 0; // so we don't add them twice.
+       } 
+
+
+  // import from other classes if required.
+
+  if (bc->l_baselist)
+  {
+    // this class requires other base classes.
+       
+    for (GSList *bclst = bc->l_baselist; bclst != NULL; bclst = bclst->next)
+    {      
+      char *requestedclass = (char *)bclst->data;
+
+//      class_t *rbc = Find_Class(l_classes, requestedclass, true);
+      class_t *rbc = Find_Class(l_classes, requestedclass, bc);
+
+      // make sure we don't request ourself!
+      if (rbc == bc)
+      {
+        Sys_Printf ("WARNING: baseclass '%s' tried to request itself!\n", bclst->data);
+      }
+                 else
+      {
+        if (!rbc)
+        {
+          Sys_Printf ("WARNING: could not find the requested baseclass '%s' when building '%s'\n", requestedclass,bc->classname);
+       }
+       else
+        {
+          // call ourself!
+          EClass_ImportFromClass(e, l_classes, rbc);
+        }
+       }
+    }
+  }
+  // SIZE
+  if (bc->gotsize)
+  {
+    e->fixedsize = true;
+    memcpy(e->mins,bc->boundingbox[0],sizeof( vec3_t ));
+    memcpy(e->maxs,bc->boundingbox[1],sizeof( vec3_t ));
+  }
+/*
+  // Hydra: apparently, this would be bad.
+
+  if (bc->sprite)
+  {
+    // Hydra - NOTE: e->skinpath is not currently used by the editor but the code
+    // to set it is used by both this eclass_t loader and the .DEF eclass_t loader.
+    // TODO: implement using e->skinpath.
+    if (e->skinpath)
+      free (e->skinpath); 
+       
+    e->skinpath = strdup(bc->sprite);
+  }
+*/
+
+  // MODEL
+  if (bc->model)
+  {
+    if (e->modelpath)
+      free (e->modelpath);
+
+    e->modelpath = strdup(bc->model);
+  }
+
+  // COLOR
+  if (bc->gotcolor)
+  {
+    memcpy(e->color,bc->color,sizeof( vec3_t ));
+    sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
+       e->texdef.SetName(color);
+  }
+
+  // SPAWNFLAGS and COMMENTS
+  if (bc->l_optionlist)
+  {
+    for (GSList *optlst = bc->l_optionlist; optlst != NULL; optlst = optlst->next)
+    {
+      option_t *opt = (option_t*) optlst->data;
+       
+      if (opt->optiontype != OPTION_FLAGS)
+      {
+        // add some info to the comments.
+        if (opt->optioninfo)
+        {
+          sprintf(newcomments+strlen(newcomments),"%s '%s' %s%s\n",
+            opt->epairname, 
+            opt->optioninfo ? opt->optioninfo : "", 
+            opt->optiondefault ? ", Default: " : "",
+            opt->optiondefault ? opt->optiondefault : "");      
+        }
+        else
+        {
+          sprintf(newcomments+strlen(newcomments),"%s %s%s\n",
+            opt->epairname, 
+            opt->optiondefault ? ", Default: " : "",
+            opt->optiondefault ? opt->optiondefault : "");      
+        }
+      }
+
+      GSList *choicelst;
+      switch(opt->optiontype)
+      {        
+        case OPTION_FLAGS :
+          // grab the flags.
+          for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)
+          {
+            choice_t *choice = (choice_t*) choicelst->data;
+       
+            int index = getindex(choice->value);
+            index--;
+            if (index < MAX_FLAGS)
+            {
+              strcpy(e->flagnames[index],choice->name);
+            }
+            else
+                 {
+              Sys_Printf ("WARNING: baseclass '%s' has a spawnflag out of range, ignored!\n", bc->classname);
+            }
+          }
+                           break;
+        case OPTION_CHOICES :
+          strcat(newcomments,"  Choices:\n");
+          for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)
+          {
+            choice_t *choice = (choice_t*) choicelst->data;
+            sprintf(newcomments+strlen(newcomments),"  %5d - %s\n",choice->value,choice->name);
+          }       
+          break;
+      }
+    }
+       }
+
+  // name
+  if (e->name) free(e->name);
+  e->name = strdup(bc->classname);
+       
+  // fixed size initialisation
+  if (bc->classtype == CLASS_POINTCLASS)
+  {    
+    e->fixedsize = true;
+    // some point classes dont seem to have size()'s in the fgd, so set up a default here..
+    if ((e->mins[0] == 0) && (e->mins[1] == 0) && (e->mins[2] == 0) && 
+        (e->maxs[0] == 0) && (e->maxs[1] == 0) && (e->maxs[2] == 0))
+  {
+      e->mins[0] = -8;
+      e->mins[1] = -8;
+      e->mins[2] = -8;
+      e->maxs[0] = 8;
+      e->maxs[1] = 8;
+      e->maxs[2] = 8;
+  }
+
+    if (e->texdef.GetName() == NULL)
+    {
+      // no color specified for this entity in the fgd file
+      // set one now
+      // Note: if this eclass_t is not fully built, then this may be
+      // overridden with the correct color.
+      
+      e->color[0] = 1;
+      e->color[1] = 0.5; // how about a nice bright pink, mmm, nice! :)
+      e->color[2] = 1;
+
+      sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
+       e->texdef.SetName(color);
+    }
+  }
+
+  // COMMENTS
+  if (newcomments[0])
+  {
+    e->comments = addstr(e->comments,newcomments);
+  }
+}
+
+/*!
+create the eclass_t structures and add to the global list.
+*/
+void Create_EClasses(GSList *l_classes)
+{
+  int count = 0;
+  // loop through the loaded structures finding all the non BaseClasses
+  for (GSList *clst = l_classes; clst != NULL; clst = clst->next)
+  {
+    class_t *c = (class_t *)clst->data;
+
+    if (c->classtype == CLASS_BASECLASS) // not looking for these.
+      continue;
+
+    eclass_t *e = (eclass_t *) malloc( sizeof( eclass_s ));
+    memset(e,0,sizeof( eclass_s ));
+
+    EClass_ImportFromClass(e, l_classes, c );
+
+    // radiant will crash if this is null, and it still could be at this point.
+    if (!e->comments)
+    {
+      e->comments=strdup("No description available, check documentation\n");
+    }
+
+    // dump the spawnflags to the end of the comments.
+    int i;
+    bool havespawnflags;
+
+    havespawnflags = false;
+    for (i = 0 ; i < MAX_FLAGS ; i++)
+    {
+      if (*e->flagnames[i]) havespawnflags = true;
+    }
+
+    if (havespawnflags)
+    {
+      char spawnline[80];
+      e->comments = addstr(e->comments,"Spawnflags\n");
+      for (i = 0 ; i < MAX_FLAGS ; i++)
+      {
+        if (*e->flagnames[i]) 
+        {
+          sprintf(spawnline,"  %d - %s\n", 1<<i, e->flagnames[i]); 
+          e->comments = addstr(e->comments,spawnline);
+        }
+      }
+    }
+
+    Eclass_InsertAlphabetized (e);
+    count ++;
+    // Hydra: ttimo, I don't think this code is required...
+               // single ?
+               *Get_Eclass_E() = e;
+    Set_Eclass_Found(true);
+               if ( Get_Parsing_Single() )
+                       break;
+  }
+
+  Sys_Printf ("FGD Loaded %d entities.\n", count);
+}
+
+void Eclass_ScanFile (char *filename)
+{
+       int             size;
+       char    *data;
+       char    temp[1024];
+  GSList *l_classes = NULL;
+       char  token_debug[1024]; //++Hydra FIXME: cleanup this.
+  bool done = false;
+  int len,classtype;
+
+  char *token = Token();
+       
+       QE_ConvertDOSToUnixName( temp, filename );
+       
+       size = vfsLoadFullPathFile (filename, (void**)&data);
+  if (size <= 0)
+  {
+    Sys_FPrintf (SYS_ERR, "Eclass_ScanFile: %s not found\n", filename);
+    return;
+  }
+       Sys_Printf ("ScanFile: %s\n", temp);    
+
+  // start parsing the file
+  StartTokenParsing(data);
+
+  // build a list of base classes first
+
+  while (!done)
+               {
+    // find an @ sign.
+    do
+    {
+      if (!GetToken(true))
+      {
+        done = true;
+        break;
+      }
+    } while (token[0] != '@');
+
+    strcpy(temp,token+1); // skip the @
+
+    classtype = CLASS_NOCLASS;
+    if (!stricmp(temp,"BaseClass")) classtype = CLASS_BASECLASS;
+    if (!stricmp(temp,"PointClass")) classtype = CLASS_POINTCLASS;
+    if (!stricmp(temp,"SolidClass")) classtype = CLASS_SOLIDCLASS;
+
+    if (classtype)
+    {
+      class_t *newclass = (class_t *) malloc( sizeof(class_s) );
+      memset( newclass, 0, sizeof(class_s) );
+      newclass->classtype = classtype;
+
+      while (1)
+      {
+        GetTokenExtra(false,"(",false); // option or =
+        strcpy(token_debug,token);
+
+        if (!strcmp(token,"="))
+        {
+          UnGetToken();
+          break;
+        }
+                       else
+        {
+          strlower(token);
+          if (!strcmp(token,"base"))
+          {
+            GetTokenExtra(false,"(",true); // (
+
+            if (!strcmp(token,"("))
+            {
+              while (GetTokenExtra(false,",)",false)) // option) or option,
+              {
+                newclass->l_baselist = g_slist_append (newclass->l_baselist, strdup(token));
+
+                GetTokenExtra(false,",)",true); // , or )
+                if (!strcmp(token,")"))
+                  break;
+
+              }
+            }
+          }
+          else if (!strcmp(token,"size"))
+          {
+            // parse (w h d) or (x y z, x y z)
+
+            GetTokenExtra(false,"(",true); // (
+            if (!strcmp(token,"("))
+            {
+              int sizedone = false;
+              float w,h,d;
+              GetToken(false);
+              w = atof(token);
+              GetToken(false);
+              h = atof(token);
+              GetToken(false); // number) or number ,
+              strcpy(temp,token);
+              len = strlen(temp);
+              if (temp[len-1] == ')') sizedone = true;                
+              temp[len-1] = 0;
+              d = atof(temp);
+              if (sizedone)
+              {
+                // only one set of cordinates supplied, change the W,H,D to mins/maxs
+                newclass->boundingbox[0][0] = 0 - (w / 2);
+                newclass->boundingbox[1][0] = w / 2;
+                newclass->boundingbox[0][1] = 0 - (h / 2);
+                newclass->boundingbox[1][1] = h / 2;
+                newclass->boundingbox[0][2] = 0 - (d / 2);
+                newclass->boundingbox[1][2] = d / 2;
+                newclass->gotsize = true;
+              }
+              else
+              {
+                newclass->boundingbox[0][0] = w;
+                newclass->boundingbox[0][1] = h;
+                newclass->boundingbox[0][2] = d;
+                GetToken(false);
+                newclass->boundingbox[1][0] = atof(token);
+                GetToken(false);
+                newclass->boundingbox[1][1] = atof(token);
+/*
+                GetToken(false); // "number)" or "number )"
+                strcpy(temp,token);
+                len = strlen(temp);
+                if (temp[len-1] == ')') 
+                  temp[len-1] = 0;
+                else
+                  GetToken(false); // )
+                newclass->boundingbox[1][2] = atof(temp);
+*/
+                GetTokenExtra(false,")",false); // number
+                newclass->boundingbox[1][2] = atof(token);
+                newclass->gotsize = true;
+                GetTokenExtra(false,")",true); // )
+              }
+            }
+          }
+          else if (!strcmp(token,"color"))
+          {
+            GetTokenExtra(false,"(",true); // (
+            if (!strcmp(token,"("))
+            {
+              // get the color values (0-255) and normalize them if required.
+              GetToken(false);
+              newclass->color[0] = atof(token);
+              if (newclass->color[0] > 1)
+                newclass->color[0]/=255;
+              GetToken(false);
+              newclass->color[1] = atof(token);
+              if (newclass->color[1] > 1)
+                newclass->color[1]/=255;
+              GetToken(false);
+              strcpy(temp,token);
+              len = strlen(temp);
+              if (temp[len-1] == ')') temp[len-1] = 0;
+              newclass->color[2] = atof(temp);
+              if (newclass->color[2] > 1)
+                newclass->color[2]/=255;
+              newclass->gotcolor = true;
+            }
+          }
+          else if (!strcmp(token,"iconsprite"))
+          {
+            GetTokenExtra(false,"(",true); // (
+            if (!strcmp(token,"("))
+            {
+              GetTokenExtra(false,")",false); // filename)
+              // the model plugins will handle sprites too.
+              // newclass->sprite = strdup(token);
+              newclass->model = strdup(token);
+              GetTokenExtra(false,")",true); // )
+            }
+          }
+          else if (!strcmp(token,"model"))
+          {
+            GetTokenExtra(false,"(",true); // (
+            if (!strcmp(token,"("))
+            {
+              GetTokenExtra(false,")",false); // filename)
+              newclass->model = strdup(token);
+              GetTokenExtra(false,")",true); // )
+            }
+          }
+          else 
+          {
+            // Unsupported
+            GetToken(false); // skip it.
+          }
+
+        }
+      }
+
+      GetToken(false); // =
+      strcpy(token_debug,token);
+      if (!strcmp(token,"="))
+      {
+        GetToken(false);
+        newclass->classname = strdup(token);
+      }
+
+      // Get the description
+      if (newclass->classtype != CLASS_BASECLASS)
+      {
+        GetToken(false);
+        if (!strcmp(token,":"))
+        {
+          GetToken(false);
+          newclass->description = strdup(token);
+        } else UnGetToken(); // no description
+      }
+
+      // now build the option list.
+      GetToken(true); // [ or []
+
+      if (strcmp(token,"[]"))  // got some options ?
+      {
+        if (!strcmp(token,"["))
+        {
+          // yup
+          bool optioncomplete = false;
+          option_t *newoption;
+
+          while (1)
+          {
+            GetToken(true);
+            if (!strcmp(token,"]"))
+              break; // no more options
+
+            // parse the data and build the option_t
+
+            strcpy(temp,token);
+            len = strlen(temp);
+            char *ptr = strchr(temp,'(');
+
+            if (!ptr)
+                                     break;
+
+            newoption = (option_t *) malloc ( sizeof( option_s ));
+            memset( newoption, 0, sizeof( option_s ));
+
+            *ptr++ = 0;
+            newoption->epairname = strdup(temp);
+
+            len = strlen(ptr);
+            if (ptr[len-1] != ')')
+              break;
+
+            ptr[len-1] = 0;
+            strlower(ptr);
+            if (!strcmp(ptr,"integer"))
+            {
+              newoption->optiontype = OPTION_INTEGER;
+            }
+            else if (!strcmp(ptr,"choices"))
+            {
+              newoption->optiontype = OPTION_CHOICES;
+            }
+            else if (!strcmp(ptr,"flags"))
+            {
+              newoption->optiontype = OPTION_FLAGS;
+            }
+            else // string
+            {
+              newoption->optiontype = OPTION_STRING;
+            }
+
+            switch (newoption->optiontype)
+            {
+              case OPTION_STRING :
+              case OPTION_INTEGER :
+                if (!TokenAvailable())
+                {
+                  optioncomplete = true;
+                               break;
+               }
+                GetToken(false); // :
+                strcpy(token_debug,token);
+                if ((token[0] == ':') && (strlen(token) > 1))
+                {
+                  newoption->optioninfo = strdup(token+1);
+                }
+                else
+                {
+                  GetToken(false);
+                  newoption->optioninfo = strdup(token);
+                }
+                if (TokenAvailable()) // default value ?
+                {
+                  GetToken(false);
+                  if (!strcmp(token,":"))
+                  {
+                    if (GetToken(false))
+                    {
+                      newoption->optiondefault = strdup(token);
+                      optioncomplete = true;
+                    }
+                  }
+                }
+                else
+                {
+                  optioncomplete = true;
+                }
+                break;
+
+              case OPTION_CHOICES :
+                GetTokenExtra(false,":",true); // : or :"something like this" (bah!)
+                strcpy(token_debug,token);
+                if ((token[0] == ':') && (strlen(token) > 1))
+                {
+                  if (token[1] == '\"')
+                  {
+                    strcpy(temp,token+2);
+                    while (1)
+                    {
+                      if (!GetToken(false))
+                        break;
+                      strcat(temp," ");
+                      strcat(temp,token);
+                      len = strlen(temp);
+                      if (temp[len-1] == '\"')
+                      {
+                        temp[len-1] = 0;
+                        break;
+                      }
+                    }
+                  }
+                  newoption->optioninfo = strdup(temp);
+                }
+                else
+                {
+                  GetToken(false);
+                  newoption->optioninfo = strdup(token);
+                }
+                GetToken(false); // : or =
+                strcpy(token_debug,token);
+                if (!strcmp(token,":"))
+                {
+                  GetToken(false);
+                  newoption->optiondefault = strdup(token);
+                }
+                else
+                {
+                  UnGetToken();
+                }
+                // And Follow on...
+              case OPTION_FLAGS :
+                GetToken(false); // : or =
+                strcpy(token_debug,token);
+                if (strcmp(token,"=")) // missing ?
+                  break;
+
+                GetToken(true); // [
+                strcpy(token_debug,token);
+                if (strcmp(token,"[")) // missing ?
+                  break;
+
+                choice_t *newchoice;
+                while (1)
+                {
+                  GetTokenExtra(true,":",true); // "]" or "number", or "number:"
+                  strcpy(token_debug,token);
+                  if (!strcmp(token,"]")) // no more ?
+                  {
+                    optioncomplete = true;
+                    break;
+                  }
+                  strcpy(temp,token);
+                  len = strlen(temp);
+                  if (temp[len-1] == ':')
+                  {
+                    temp[len-1] = 0;
+                  }
+                  else
+                  {
+                    GetToken(false); // :
+                    if (strcmp(token,":")) // missing ?
+                      break;
+                  }
+                  if (!TokenAvailable())
+                    break;
+                  GetToken(false); // the name
+
+                  newchoice = (choice_t *) malloc ( sizeof( choice_s ));
+                  memset( newchoice, 0, sizeof( choice_s ));
+
+                  newchoice->value = atoi(temp);
+                  newchoice->name = strdup(token);
+
+                  newoption->choices = g_slist_append(newoption->choices, newchoice);
+
+                  // ignore any remaining tokens on the line
+                  while (TokenAvailable()) GetToken(false);
+
+                  // and it we found a "]" on the end of the line, put it back in the queue.
+                  if (!strcmp(token,"]")) UnGetToken();
+                }
+                               break;
+
+            }
+
+            // add option to the newclass
+
+            if (optioncomplete)
+            {
+              if (newoption)
+              {
+                // add it to the list.
+                newclass->l_optionlist = g_slist_append(newclass->l_optionlist, newoption);
+              }
+            }
+            else
+            {
+              Sys_Printf ("%WARNING: Parse error occured in '%s - %s'\n",classnames[newclass->classtype],newclass->classname);
+              Free_Option(newoption);
+            }
+
+          }
+        }
+        else
+        {
+          UnGetToken(); // shouldn't get here.
+        }
+      }
+
+      // add it to our list.
+      l_classes = g_slist_append (l_classes, newclass);
+
+    }
+  }
+
+  // finished with the file now.
+  g_free(data);
+
+  Sys_Printf ("FGD scan complete, building entities...\n");
+
+  // Once we get here we should have a few (!) lists in memory that we
+  // can extract all the information required to build a the eclass_t structures.
+
+  Create_EClasses(l_classes);
+
+  // Free everything
+
+  GSList *p = l_classes;
+  while (p)
+  {
+    class_t *tmpclass = (class_t *)p->data;
+
+#ifdef FGD_VERBOSE
+    // DEBUG: dump the info...
+    Sys_Printf ("%s: %s (", classnames[tmpclass->classtype],tmpclass->classname);
+    for (GSList *tmp = tmpclass->l_baselist; tmp != NULL; tmp = tmp->next)
+    {
+      if (tmp != tmpclass->l_baselist)
+      {
+        Sys_Printf (", ");
+      }
+      Sys_Printf ("%s", (char *)tmp->data);
+    }
+    if (tmpclass->gotsize)
+    {
+      sprintf(temp,"(%.0f %.0f %.0f) - (%.0f %.0f %.0f)",tmpclass->boundingbox[0][0],
+        tmpclass->boundingbox[0][1],
+        tmpclass->boundingbox[0][2],
+        tmpclass->boundingbox[1][0],
+        tmpclass->boundingbox[1][1],
+        tmpclass->boundingbox[1][2]);
+    } else strcpy(temp,"No Size");
+    Sys_Printf (") '%s' Size: %s",tmpclass->description ? tmpclass->description : "No description",temp);
+    if (tmpclass->gotcolor)
+    {
+      sprintf(temp,"(%d %d %d)",tmpclass->color[0],
+        tmpclass->color[1],
+        tmpclass->color[2]);
+    } else strcpy(temp,"No Color");
+    Sys_Printf (" Color: %s Options:\n",temp);
+    if (!tmpclass->l_optionlist)
+    {
+      Sys_Printf ("  No Options\n");
+    }
+    else
+    {
+      option_t *tmpoption;
+      int count;
+      GSList *olst;
+      for (olst = tmpclass->l_optionlist, count = 1; olst != NULL; olst = olst->next, count ++)
+      {
+        tmpoption = (option_t *)olst->data;
+        Sys_Printf ("  %d, Type: %s, EPair: %s\n", count,optionnames[tmpoption->optiontype], tmpoption->epairname );
+
+        choice_t *tmpchoice;
+        GSList *clst;
+        int ccount;
+        for (clst = tmpoption->choices, ccount = 1; clst != NULL; clst = clst->next, ccount ++)
+        {
+          tmpchoice = (choice_t *)clst->data;
+          Sys_Printf ("    %d, Value: %d, Name: %s\n", ccount, tmpchoice->value, tmpchoice->name);
+        }
+      }
+    }
+
+#endif
+
+    // free the baselist.
+    ClearGSList(tmpclass->l_baselist);
+    Free_Class (tmpclass);
+    p = g_slist_remove (p, p->data);
+               }
+               
+}