X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;ds=sidebyside;f=plugins%2Feclassfgd%2Fplugin.cpp;h=fa8920af18f2153e082dde8a7b035c9df89f24d9;hb=48410b113dd2036e69dbf723a39ec9af02fc9b12;hp=9b662f875fb0f9800c6a4b603a9340cda5f91e89;hpb=4189d27b34641ad3325d724f6cac012fa742b2ad;p=xonotic%2Fnetradiant.git diff --git a/plugins/eclassfgd/plugin.cpp b/plugins/eclassfgd/plugin.cpp index 9b662f87..fa8920af 100644 --- a/plugins/eclassfgd/plugin.cpp +++ b/plugins/eclassfgd/plugin.cpp @@ -1,1141 +1,1141 @@ -/* -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() 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; - -#if __GNUC__ >= 4 -#pragma GCC visibility push(default) -#endif -extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) { -#if __GNUC__ >= 4 -#pragma GCC visibility pop -#endif - 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< 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<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); - } - -} +/* +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() 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; + +#if __GNUC__ >= 4 +#pragma GCC visibility push(default) +#endif +extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) { +#if __GNUC__ >= 4 +#pragma GCC visibility pop +#endif + 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< 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<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); + } + +}