2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
22 //#define FGD_VERBOSE // define this for extra info in the log.
\r
26 /*! \file plugin.cpp
\r
27 \brief .fgd entity description format
\r
29 FGD loading code by Dominic Clifton - Hydra (Hydra@Hydras-World.com)
\r
34 This module loads .fgd files, fgd files are split into classes:
\r
37 point classes (aka fixed size entities)
\r
38 solid classes (aka brush entity)
\r
40 This program first scans each file, building up a list structures
\r
41 in memory that contain the information for all the classes found
\r
44 Then the program looks at each of the non-base classes in the list
\r
45 and build the eclass_t structure from each one.
\r
47 Classes can request information in other classes.
\r
49 Solid/Base and Point/Base classes can have the same names as other
\r
50 classes but there can be only one of each solid/point/base class with
\r
55 this is NOT allowed:
\r
60 this is NOT allowed:
\r
73 v0.1 - 13/March/2002
\r
76 v0.2 - 16/March/2002
\r
77 - sets e->skinpath when it finds an iconsprite(<filename>) token.
\r
79 v0.3 - 21/March/2002
\r
80 - Core now supports > 8 spawnflags, changes reflected here too.
\r
81 - FIXED: mins/maxs were backwards when only w,h,d were in the FGD
\r
82 (as opposed to the actual mins/maxs being in the .def file)
\r
83 - Made sure all PointClass entities were fixed size entities
\r
84 and gave them a default bounding box size if a size() setting
\r
85 was not in the FGD file.
\r
86 - Removed the string check for classes requesting another class
\r
87 with the same name, adjusted Find_Class() so that it can search
\r
88 for baseclasses only, this fixes the problem with PointClass "light"
\r
89 requesting the BaseClass "light".
\r
91 v0.4 - 25/March/2002
\r
92 - bleh, It turns out that non-baseclasses can request non-baseclasses
\r
93 so now I've changed Find_Class() so that it can ignore a specific class
\r
94 (i.e. the one that's asking for others, so that classes can't request
\r
95 themselves but they can request other classes of any kind with the
\r
97 - made all spawnflag comments appear in one place, rather than being scattered
\r
98 all over the comments if a requested class also had some spawnflags
\r
100 v0.5 - 6/April/2002
\r
101 - not using skinpath for sprites anymore, apprently we can code a model
\r
102 module to display sprites and model files.
\r
103 - model() tags are now supported.
\r
108 * add support for setting the eclass_t's modelpath.
\r
109 (not useful for CS, but very useful for HL).
\r
111 * need to implement usage for e->skinpath in the core.
\r
113 * cleanup some areas now that GetTokenExtra() is available
\r
114 (some parts were written prior to it's creation).
\r
116 * Import the comments between each BaseClass's main [ ] set.
\r
117 (unfortunatly they're // cstyle comments, which GetToken skips over)
\r
118 But still ignore comments OUTSIDE the main [ ] set.
\r
122 _QERScripLibTable g_ScripLibTable;
\r
123 _EClassManagerTable g_EClassManagerTable;
\r
124 _QERFuncTable_1 g_FuncTable;
\r
125 _QERFileSystemTable g_FileSystemTable;
\r
128 void Eclass_ScanFile (char *filename);
\r
130 const char* EClass_GetExtension()
\r
135 CSynapseServer* g_pSynapseServer = NULL;
\r
136 CSynapseClientFGD g_SynapseClient;
\r
138 extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces (const char *version, CSynapseServer *pServer)
\r
140 if (strcmp(version, SYNAPSE_VERSION))
\r
142 Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version);
\r
145 g_pSynapseServer = pServer;
\r
146 g_pSynapseServer->IncRef();
\r
147 Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());
\r
149 g_SynapseClient.AddAPI(ECLASS_MAJOR, "fgd", sizeof(_EClassTable));
\r
150 g_SynapseClient.AddAPI(SCRIPLIB_MAJOR, NULL, sizeof(g_ScripLibTable), SYN_REQUIRE, &g_ScripLibTable);
\r
151 g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);
\r
152 g_SynapseClient.AddAPI(ECLASSMANAGER_MAJOR, NULL, sizeof(g_EClassManagerTable), SYN_REQUIRE, &g_EClassManagerTable);
\r
154 // Needs a 'default' option for this minor because we certainly don't load anything from wad files :)
\r
155 g_SynapseClient.AddAPI(VFS_MAJOR, "wad", sizeof(g_FileSystemTable), SYN_REQUIRE, &g_FileSystemTable);
\r
157 return &g_SynapseClient;
\r
160 bool CSynapseClientFGD::RequestAPI(APIDescriptor_t *pAPI)
\r
162 if (!strcmp(pAPI->major_name, ECLASS_MAJOR))
\r
164 _EClassTable* pTable= static_cast<_EClassTable*>(pAPI->mpTable);
\r
166 pTable->m_pfnGetExtension = &EClass_GetExtension;
\r
167 pTable->m_pfnScanFile = &Eclass_ScanFile;
\r
172 Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
\r
176 #include "version.h"
\r
178 const char* CSynapseClientFGD::GetInfo()
\r
180 return ".fgd eclass module built " __DATE__ " " RADIANT_VERSION;
\r
183 // ------------------------------------------------------------------------------------------------
\r
185 #define CLASS_NOCLASS 0
\r
186 #define CLASS_BASECLASS 1
\r
187 #define CLASS_POINTCLASS 2
\r
188 #define CLASS_SOLIDCLASS 3
\r
190 char *classnames[] = {"NOT DEFINED","BaseClass","PointClass","SolidClass"};
\r
192 #define OPTION_NOOPTION 0
\r
193 #define OPTION_STRING 1
\r
194 #define OPTION_CHOICES 2
\r
195 #define OPTION_INTEGER 3
\r
196 #define OPTION_FLAGS 4
\r
198 char *optionnames[] = {"NOT DEFINED","String","Choices","Integer","Flags"};
\r
200 typedef struct choice_s {
\r
205 typedef struct option_s {
\r
209 char *optiondefault;
\r
210 GSList *choices; // list of choices_t
\r
213 typedef struct class_s {
\r
214 int classtype; // see CLASS_* above.
\r
216 GSList *l_baselist; // when building the eclass_t, other class_s's with these names are required.
\r
219 GSList *l_optionlist; // when building the eclass_t, other class_s's with these names are required.
\r
221 bool gotsize; // if set then boundingbox is valid.
\r
222 vec3_t boundingbox[2]; // mins, maxs
\r
224 bool gotcolor; // if set then color is valid.
\r
225 vec3_t color; // R,G,B, loaded as 0-255
\r
227 char *model; // relative path + filename to a model (.spr/.mdl) file, or NULL
\r
231 ===========================================================
\r
234 ===========================================================
\r
236 char *strlower (char *start)
\r
242 *in = tolower(*in);
\r
248 char *addstr(char *dest,char *source)
\r
253 int len = strlen(dest);
\r
254 ptr = (char *) malloc (len + strlen(source) + 1);
\r
256 strcpy(ptr+len,source);
\r
262 dest = strdup(source);
\r
267 int getindex(unsigned int a)
\r
269 unsigned int count = 0;
\r
270 unsigned int b = 0;
\r
271 for (;b != a;count++)
\r
280 void ClearGSList (GSList* lst)
\r
286 p = g_slist_remove (p, p->data);
\r
291 free a choice_t structure and it's contents
\r
293 void Free_Choice(choice_t *p)
\r
295 if (p->name) free(p->name);
\r
301 ===========================================================
\r
304 ===========================================================
\r
308 free an option_t structure and it's contents
\r
310 void Free_Option(option_t *p)
\r
312 if (p->epairname) free(p->epairname);
\r
313 if (p->optiondefault) free(p->optiondefault);
\r
314 if (p->optioninfo) free(p->optioninfo);
\r
315 GSList *l = p->choices;
\r
318 Free_Choice ((choice_t *)l->data);
\r
319 l = g_slist_remove (l, l->data);
\r
325 free a class_t structure and it's contents
\r
327 void Free_Class(class_t *p)
\r
329 GSList *l = p->l_optionlist;
\r
332 Free_Option ((option_t *)l->data);
\r
333 l = g_slist_remove (l, l->data);
\r
336 if (p->classname) free(p->classname);
\r
341 find a class in the list
\r
343 class_t *Find_Class(GSList *l,char *classname, class_t *ignore)
\r
345 for (GSList *clst = l; clst != NULL; clst = clst->next)
\r
347 class_t *c = (class_t *)clst->data;
\r
352 // NOTE: to speed up we could make all the classnames lower-case when they're initialised.
\r
353 if (!stricmp(c->classname,classname))
\r
363 Import as much as possible from a class_t into an eclass_t
\r
364 Note: this is somewhat recursive, as a class can require a class that requires a class and so on..
\r
366 void EClass_ImportFromClass(eclass_t *e, GSList *l_classes, class_t *bc)
\r
370 // We allocate 16k here, but only the memory actually used is kept allocated.
\r
371 // this is just used for building the final comments string.
\r
372 // Note: if the FGD file contains comments that are >16k (per entity) then
\r
373 // radiant will crash upon loading such a file as the eclass_t will become
\r
375 // FIXME: we could add some length checking when building "newcomments", but
\r
376 // that'd slow it down a bit.
\r
377 char newcomments[16384] = "";
\r
379 //Note: we override the values already in e.
\r
380 //and we do it in such a way that the items that appear last in the l_baselist
\r
381 //represent the final values.
\r
383 if (bc->description)
\r
385 sprintf(newcomments,"%s\n",bc->description);
\r
386 e->comments = addstr(e->comments,newcomments);
\r
387 newcomments[0] = 0; // so we don't add them twice.
\r
391 // import from other classes if required.
\r
393 if (bc->l_baselist)
\r
395 // this class requires other base classes.
\r
397 for (GSList *bclst = bc->l_baselist; bclst != NULL; bclst = bclst->next)
\r
399 char *requestedclass = (char *)bclst->data;
\r
401 // class_t *rbc = Find_Class(l_classes, requestedclass, true);
\r
402 class_t *rbc = Find_Class(l_classes, requestedclass, bc);
\r
404 // make sure we don't request ourself!
\r
407 Sys_Printf ("WARNING: baseclass '%s' tried to request itself!\n", bclst->data);
\r
413 Sys_Printf ("WARNING: could not find the requested baseclass '%s' when building '%s'\n", requestedclass,bc->classname);
\r
418 EClass_ImportFromClass(e, l_classes, rbc);
\r
426 e->fixedsize = true;
\r
427 memcpy(e->mins,bc->boundingbox[0],sizeof( vec3_t ));
\r
428 memcpy(e->maxs,bc->boundingbox[1],sizeof( vec3_t ));
\r
431 // Hydra: apparently, this would be bad.
\r
435 // Hydra - NOTE: e->skinpath is not currently used by the editor but the code
\r
436 // to set it is used by both this eclass_t loader and the .DEF eclass_t loader.
\r
437 // TODO: implement using e->skinpath.
\r
439 free (e->skinpath);
\r
441 e->skinpath = strdup(bc->sprite);
\r
449 free (e->modelpath);
\r
451 e->modelpath = strdup(bc->model);
\r
457 memcpy(e->color,bc->color,sizeof( vec3_t ));
\r
458 sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
\r
459 e->texdef.SetName(color);
\r
462 // SPAWNFLAGS and COMMENTS
\r
463 if (bc->l_optionlist)
\r
465 for (GSList *optlst = bc->l_optionlist; optlst != NULL; optlst = optlst->next)
\r
467 option_t *opt = (option_t*) optlst->data;
\r
469 if (opt->optiontype != OPTION_FLAGS)
\r
471 // add some info to the comments.
\r
472 if (opt->optioninfo)
\r
474 sprintf(newcomments+strlen(newcomments),"%s '%s' %s%s\n",
\r
476 opt->optioninfo ? opt->optioninfo : "",
\r
477 opt->optiondefault ? ", Default: " : "",
\r
478 opt->optiondefault ? opt->optiondefault : "");
\r
482 sprintf(newcomments+strlen(newcomments),"%s %s%s\n",
\r
484 opt->optiondefault ? ", Default: " : "",
\r
485 opt->optiondefault ? opt->optiondefault : "");
\r
490 switch(opt->optiontype)
\r
492 case OPTION_FLAGS :
\r
494 for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)
\r
496 choice_t *choice = (choice_t*) choicelst->data;
\r
498 int index = getindex(choice->value);
\r
500 if (index < MAX_FLAGS)
\r
502 strcpy(e->flagnames[index],choice->name);
\r
506 Sys_Printf ("WARNING: baseclass '%s' has a spawnflag out of range, ignored!\n", bc->classname);
\r
510 case OPTION_CHOICES :
\r
511 strcat(newcomments," Choices:\n");
\r
512 for (choicelst = opt->choices; choicelst != NULL; choicelst = choicelst->next)
\r
514 choice_t *choice = (choice_t*) choicelst->data;
\r
515 sprintf(newcomments+strlen(newcomments)," %5d - %s\n",choice->value,choice->name);
\r
523 if (e->name) free(e->name);
\r
524 e->name = strdup(bc->classname);
\r
526 // fixed size initialisation
\r
527 if (bc->classtype == CLASS_POINTCLASS)
\r
529 e->fixedsize = true;
\r
530 // some point classes dont seem to have size()'s in the fgd, so set up a default here..
\r
531 if ((e->mins[0] == 0) && (e->mins[1] == 0) && (e->mins[2] == 0) &&
\r
532 (e->maxs[0] == 0) && (e->maxs[1] == 0) && (e->maxs[2] == 0))
\r
542 if (e->texdef.GetName() == NULL)
\r
544 // no color specified for this entity in the fgd file
\r
546 // Note: if this eclass_t is not fully built, then this may be
\r
547 // overridden with the correct color.
\r
550 e->color[1] = 0.5; // how about a nice bright pink, mmm, nice! :)
\r
553 sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
\r
554 e->texdef.SetName(color);
\r
559 if (newcomments[0])
\r
561 e->comments = addstr(e->comments,newcomments);
\r
566 create the eclass_t structures and add to the global list.
\r
568 void Create_EClasses(GSList *l_classes)
\r
571 // loop through the loaded structures finding all the non BaseClasses
\r
572 for (GSList *clst = l_classes; clst != NULL; clst = clst->next)
\r
574 class_t *c = (class_t *)clst->data;
\r
576 if (c->classtype == CLASS_BASECLASS) // not looking for these.
\r
579 eclass_t *e = (eclass_t *) malloc( sizeof( eclass_s ));
\r
580 memset(e,0,sizeof( eclass_s ));
\r
582 EClass_ImportFromClass(e, l_classes, c );
\r
584 // radiant will crash if this is null, and it still could be at this point.
\r
587 e->comments=strdup("No description available, check documentation\n");
\r
590 // dump the spawnflags to the end of the comments.
\r
592 bool havespawnflags;
\r
594 havespawnflags = false;
\r
595 for (i = 0 ; i < MAX_FLAGS ; i++)
\r
597 if (*e->flagnames[i]) havespawnflags = true;
\r
600 if (havespawnflags)
\r
602 char spawnline[80];
\r
603 e->comments = addstr(e->comments,"Spawnflags\n");
\r
604 for (i = 0 ; i < MAX_FLAGS ; i++)
\r
606 if (*e->flagnames[i])
\r
608 sprintf(spawnline," %d - %s\n", 1<<i, e->flagnames[i]);
\r
609 e->comments = addstr(e->comments,spawnline);
\r
614 Eclass_InsertAlphabetized (e);
\r
616 // Hydra: ttimo, I don't think this code is required...
\r
618 *Get_Eclass_E() = e;
\r
619 Set_Eclass_Found(true);
\r
620 if ( Get_Parsing_Single() )
\r
624 Sys_Printf ("FGD Loaded %d entities.\n", count);
\r
627 void Eclass_ScanFile (char *filename)
\r
632 GSList *l_classes = NULL;
\r
633 char token_debug[1024]; //++Hydra FIXME: cleanup this.
\r
637 char *token = Token();
\r
639 QE_ConvertDOSToUnixName( temp, filename );
\r
641 size = vfsLoadFullPathFile (filename, (void**)&data);
\r
644 Sys_FPrintf (SYS_ERR, "Eclass_ScanFile: %s not found\n", filename);
\r
647 Sys_Printf ("ScanFile: %s\n", temp);
\r
649 // start parsing the file
\r
650 StartTokenParsing(data);
\r
652 // build a list of base classes first
\r
659 if (!GetToken(true))
\r
664 } while (token[0] != '@');
\r
666 strcpy(temp,token+1); // skip the @
\r
668 classtype = CLASS_NOCLASS;
\r
669 if (!stricmp(temp,"BaseClass")) classtype = CLASS_BASECLASS;
\r
670 if (!stricmp(temp,"PointClass")) classtype = CLASS_POINTCLASS;
\r
671 if (!stricmp(temp,"SolidClass")) classtype = CLASS_SOLIDCLASS;
\r
675 class_t *newclass = (class_t *) malloc( sizeof(class_s) );
\r
676 memset( newclass, 0, sizeof(class_s) );
\r
677 newclass->classtype = classtype;
\r
681 GetTokenExtra(false,"(",false); // option or =
\r
682 strcpy(token_debug,token);
\r
684 if (!strcmp(token,"="))
\r
692 if (!strcmp(token,"base"))
\r
694 GetTokenExtra(false,"(",true); // (
\r
696 if (!strcmp(token,"("))
\r
698 while (GetTokenExtra(false,",)",false)) // option) or option,
\r
700 newclass->l_baselist = g_slist_append (newclass->l_baselist, strdup(token));
\r
702 GetTokenExtra(false,",)",true); // , or )
\r
703 if (!strcmp(token,")"))
\r
709 else if (!strcmp(token,"size"))
\r
711 // parse (w h d) or (x y z, x y z)
\r
713 GetTokenExtra(false,"(",true); // (
\r
714 if (!strcmp(token,"("))
\r
716 int sizedone = false;
\r
722 GetToken(false); // number) or number ,
\r
723 strcpy(temp,token);
\r
724 len = strlen(temp);
\r
725 if (temp[len-1] == ')') sizedone = true;
\r
730 // only one set of cordinates supplied, change the W,H,D to mins/maxs
\r
731 newclass->boundingbox[0][0] = 0 - (w / 2);
\r
732 newclass->boundingbox[1][0] = w / 2;
\r
733 newclass->boundingbox[0][1] = 0 - (h / 2);
\r
734 newclass->boundingbox[1][1] = h / 2;
\r
735 newclass->boundingbox[0][2] = 0 - (d / 2);
\r
736 newclass->boundingbox[1][2] = d / 2;
\r
737 newclass->gotsize = true;
\r
741 newclass->boundingbox[0][0] = w;
\r
742 newclass->boundingbox[0][1] = h;
\r
743 newclass->boundingbox[0][2] = d;
\r
745 newclass->boundingbox[1][0] = atof(token);
\r
747 newclass->boundingbox[1][1] = atof(token);
\r
749 GetToken(false); // "number)" or "number )"
\r
750 strcpy(temp,token);
\r
751 len = strlen(temp);
\r
752 if (temp[len-1] == ')')
\r
755 GetToken(false); // )
\r
756 newclass->boundingbox[1][2] = atof(temp);
\r
758 GetTokenExtra(false,")",false); // number
\r
759 newclass->boundingbox[1][2] = atof(token);
\r
760 newclass->gotsize = true;
\r
761 GetTokenExtra(false,")",true); // )
\r
765 else if (!strcmp(token,"color"))
\r
767 GetTokenExtra(false,"(",true); // (
\r
768 if (!strcmp(token,"("))
\r
770 // get the color values (0-255) and normalize them if required.
\r
772 newclass->color[0] = atof(token);
\r
773 if (newclass->color[0] > 1)
\r
774 newclass->color[0]/=255;
\r
776 newclass->color[1] = atof(token);
\r
777 if (newclass->color[1] > 1)
\r
778 newclass->color[1]/=255;
\r
780 strcpy(temp,token);
\r
781 len = strlen(temp);
\r
782 if (temp[len-1] == ')') temp[len-1] = 0;
\r
783 newclass->color[2] = atof(temp);
\r
784 if (newclass->color[2] > 1)
\r
785 newclass->color[2]/=255;
\r
786 newclass->gotcolor = true;
\r
789 else if (!strcmp(token,"iconsprite"))
\r
791 GetTokenExtra(false,"(",true); // (
\r
792 if (!strcmp(token,"("))
\r
794 GetTokenExtra(false,")",false); // filename)
\r
795 // the model plugins will handle sprites too.
\r
796 // newclass->sprite = strdup(token);
\r
797 newclass->model = strdup(token);
\r
798 GetTokenExtra(false,")",true); // )
\r
801 else if (!strcmp(token,"model"))
\r
803 GetTokenExtra(false,"(",true); // (
\r
804 if (!strcmp(token,"("))
\r
806 GetTokenExtra(false,")",false); // filename)
\r
807 newclass->model = strdup(token);
\r
808 GetTokenExtra(false,")",true); // )
\r
814 GetToken(false); // skip it.
\r
820 GetToken(false); // =
\r
821 strcpy(token_debug,token);
\r
822 if (!strcmp(token,"="))
\r
825 newclass->classname = strdup(token);
\r
828 // Get the description
\r
829 if (newclass->classtype != CLASS_BASECLASS)
\r
832 if (!strcmp(token,":"))
\r
835 newclass->description = strdup(token);
\r
836 } else UnGetToken(); // no description
\r
839 // now build the option list.
\r
840 GetToken(true); // [ or []
\r
842 if (strcmp(token,"[]")) // got some options ?
\r
844 if (!strcmp(token,"["))
\r
847 bool optioncomplete = false;
\r
848 option_t *newoption;
\r
853 if (!strcmp(token,"]"))
\r
854 break; // no more options
\r
856 // parse the data and build the option_t
\r
858 strcpy(temp,token);
\r
859 len = strlen(temp);
\r
860 char *ptr = strchr(temp,'(');
\r
865 newoption = (option_t *) malloc ( sizeof( option_s ));
\r
866 memset( newoption, 0, sizeof( option_s ));
\r
869 newoption->epairname = strdup(temp);
\r
872 if (ptr[len-1] != ')')
\r
877 if (!strcmp(ptr,"integer"))
\r
879 newoption->optiontype = OPTION_INTEGER;
\r
881 else if (!strcmp(ptr,"choices"))
\r
883 newoption->optiontype = OPTION_CHOICES;
\r
885 else if (!strcmp(ptr,"flags"))
\r
887 newoption->optiontype = OPTION_FLAGS;
\r
891 newoption->optiontype = OPTION_STRING;
\r
894 switch (newoption->optiontype)
\r
896 case OPTION_STRING :
\r
897 case OPTION_INTEGER :
\r
898 if (!TokenAvailable())
\r
900 optioncomplete = true;
\r
903 GetToken(false); // :
\r
904 strcpy(token_debug,token);
\r
905 if ((token[0] == ':') && (strlen(token) > 1))
\r
907 newoption->optioninfo = strdup(token+1);
\r
912 newoption->optioninfo = strdup(token);
\r
914 if (TokenAvailable()) // default value ?
\r
917 if (!strcmp(token,":"))
\r
919 if (GetToken(false))
\r
921 newoption->optiondefault = strdup(token);
\r
922 optioncomplete = true;
\r
928 optioncomplete = true;
\r
932 case OPTION_CHOICES :
\r
933 GetTokenExtra(false,":",true); // : or :"something like this" (bah!)
\r
934 strcpy(token_debug,token);
\r
935 if ((token[0] == ':') && (strlen(token) > 1))
\r
937 if (token[1] == '\"')
\r
939 strcpy(temp,token+2);
\r
942 if (!GetToken(false))
\r
945 strcat(temp,token);
\r
946 len = strlen(temp);
\r
947 if (temp[len-1] == '\"')
\r
954 newoption->optioninfo = strdup(temp);
\r
959 newoption->optioninfo = strdup(token);
\r
961 GetToken(false); // : or =
\r
962 strcpy(token_debug,token);
\r
963 if (!strcmp(token,":"))
\r
966 newoption->optiondefault = strdup(token);
\r
972 // And Follow on...
\r
973 case OPTION_FLAGS :
\r
974 GetToken(false); // : or =
\r
975 strcpy(token_debug,token);
\r
976 if (strcmp(token,"=")) // missing ?
\r
979 GetToken(true); // [
\r
980 strcpy(token_debug,token);
\r
981 if (strcmp(token,"[")) // missing ?
\r
984 choice_t *newchoice;
\r
987 GetTokenExtra(true,":",true); // "]" or "number", or "number:"
\r
988 strcpy(token_debug,token);
\r
989 if (!strcmp(token,"]")) // no more ?
\r
991 optioncomplete = true;
\r
994 strcpy(temp,token);
\r
995 len = strlen(temp);
\r
996 if (temp[len-1] == ':')
\r
1002 GetToken(false); // :
\r
1003 if (strcmp(token,":")) // missing ?
\r
1006 if (!TokenAvailable())
\r
1008 GetToken(false); // the name
\r
1010 newchoice = (choice_t *) malloc ( sizeof( choice_s ));
\r
1011 memset( newchoice, 0, sizeof( choice_s ));
\r
1013 newchoice->value = atoi(temp);
\r
1014 newchoice->name = strdup(token);
\r
1016 newoption->choices = g_slist_append(newoption->choices, newchoice);
\r
1018 // ignore any remaining tokens on the line
\r
1019 while (TokenAvailable()) GetToken(false);
\r
1021 // and it we found a "]" on the end of the line, put it back in the queue.
\r
1022 if (!strcmp(token,"]")) UnGetToken();
\r
1028 // add option to the newclass
\r
1030 if (optioncomplete)
\r
1034 // add it to the list.
\r
1035 newclass->l_optionlist = g_slist_append(newclass->l_optionlist, newoption);
\r
1040 Sys_Printf ("%WARNING: Parse error occured in '%s - %s'\n",classnames[newclass->classtype],newclass->classname);
\r
1041 Free_Option(newoption);
\r
1048 UnGetToken(); // shouldn't get here.
\r
1052 // add it to our list.
\r
1053 l_classes = g_slist_append (l_classes, newclass);
\r
1058 // finished with the file now.
\r
1061 Sys_Printf ("FGD scan complete, building entities...\n");
\r
1063 // Once we get here we should have a few (!) lists in memory that we
\r
1064 // can extract all the information required to build a the eclass_t structures.
\r
1066 Create_EClasses(l_classes);
\r
1068 // Free everything
\r
1070 GSList *p = l_classes;
\r
1073 class_t *tmpclass = (class_t *)p->data;
\r
1075 #ifdef FGD_VERBOSE
\r
1076 // DEBUG: dump the info...
\r
1077 Sys_Printf ("%s: %s (", classnames[tmpclass->classtype],tmpclass->classname);
\r
1078 for (GSList *tmp = tmpclass->l_baselist; tmp != NULL; tmp = tmp->next)
\r
1080 if (tmp != tmpclass->l_baselist)
\r
1082 Sys_Printf (", ");
\r
1084 Sys_Printf ("%s", (char *)tmp->data);
\r
1086 if (tmpclass->gotsize)
\r
1088 sprintf(temp,"(%.0f %.0f %.0f) - (%.0f %.0f %.0f)",tmpclass->boundingbox[0][0],
\r
1089 tmpclass->boundingbox[0][1],
\r
1090 tmpclass->boundingbox[0][2],
\r
1091 tmpclass->boundingbox[1][0],
\r
1092 tmpclass->boundingbox[1][1],
\r
1093 tmpclass->boundingbox[1][2]);
\r
1094 } else strcpy(temp,"No Size");
\r
1095 Sys_Printf (") '%s' Size: %s",tmpclass->description ? tmpclass->description : "No description",temp);
\r
1096 if (tmpclass->gotcolor)
\r
1098 sprintf(temp,"(%d %d %d)",tmpclass->color[0],
\r
1099 tmpclass->color[1],
\r
1100 tmpclass->color[2]);
\r
1101 } else strcpy(temp,"No Color");
\r
1102 Sys_Printf (" Color: %s Options:\n",temp);
\r
1103 if (!tmpclass->l_optionlist)
\r
1105 Sys_Printf (" No Options\n");
\r
1109 option_t *tmpoption;
\r
1112 for (olst = tmpclass->l_optionlist, count = 1; olst != NULL; olst = olst->next, count ++)
\r
1114 tmpoption = (option_t *)olst->data;
\r
1115 Sys_Printf (" %d, Type: %s, EPair: %s\n", count,optionnames[tmpoption->optiontype], tmpoption->epairname );
\r
1117 choice_t *tmpchoice;
\r
1120 for (clst = tmpoption->choices, ccount = 1; clst != NULL; clst = clst->next, ccount ++)
\r
1122 tmpchoice = (choice_t *)clst->data;
\r
1123 Sys_Printf (" %d, Value: %d, Name: %s\n", ccount, tmpchoice->value, tmpchoice->name);
\r
1130 // free the baselist.
\r
1131 ClearGSList(tmpclass->l_baselist);
\r
1132 Free_Class (tmpclass);
\r
1133 p = g_slist_remove (p, p->data);
\r