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