]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/eclass.cpp
get the basics of a new scons build system together
[xonotic/netradiant.git] / radiant / eclass.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \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
11 \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
16 \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
20 */\r
21 \r
22 #include "stdafx.h"\r
23 #include <sys/stat.h>\r
24 #if defined (__linux__) || defined (__APPLE__)\r
25 #include <dirent.h>\r
26 #endif\r
27 #include "assert.h"\r
28 \r
29 eclass_t        *eclass = NULL;\r
30 eclass_t        *eclass_bad = NULL;\r
31 const vec3_t smallbox[2] = {{-8,-8,-8},{8,8,8}};\r
32 char            eclass_directory[1024];\r
33 \r
34 qboolean parsing_single = false;\r
35 eclass_t *eclass_e;\r
36 \r
37 /*!\r
38 implementation of the EClass manager API\r
39 */\r
40 eclass_t** Get_EClass_E()\r
41 {\r
42   return &eclass_e;\r
43 }\r
44 \r
45 void Set_Eclass_Found(qboolean b)\r
46 {\r
47   eclass_found = b;\r
48 }\r
49 \r
50 qboolean Get_Parsing_Single()\r
51 {\r
52   return parsing_single;\r
53 }\r
54 \r
55 \r
56 // md3 cache for misc_models\r
57 //eclass_t *g_md3Cache = NULL;\r
58 \r
59 /*\r
60 \r
61 the classname, color triple, and bounding box are parsed out of comments\r
62 A ? size means take the exact brush size.\r
63 \r
64 / *QUAKED <classname> (0 0 0) ?\r
65 / *QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)\r
66 \r
67 Flag names can follow the size description:\r
68 \r
69 / *QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY\r
70 \r
71 */\r
72 \r
73 void CleanEntityList(eclass_t *&pList)\r
74 {\r
75   while (pList)\r
76   {\r
77     eclass_t* pTemp = pList->next;\r
78 \r
79     entitymodel *model = pList->model;\r
80     while (model != NULL)\r
81     {\r
82       delete []model->pTriList;\r
83       if (model->strSkin)\r
84         g_string_free( (GString *)model->strSkin, TRUE );\r
85       model->strSkin = NULL;\r
86       model = model->pNext;\r
87     }\r
88     \r
89     if (pList->modelpath) {\r
90       free(pList->modelpath);\r
91       pList->modelpath = NULL;\r
92     }\r
93     if (pList->skinpath) {\r
94       free(pList->skinpath);\r
95       pList->skinpath = NULL;\r
96     }\r
97     \r
98     free(pList->name);\r
99     free(pList->comments);\r
100     free(pList);\r
101     pList = pTemp;\r
102   }\r
103 \r
104   pList = NULL;\r
105 \r
106 }\r
107 \r
108 \r
109 void CleanUpEntities()\r
110 {\r
111   // NOTE: maybe some leak checks needed .. older versions of Radiant looked like they were freezing more stuff\r
112   CleanEntityList(eclass);\r
113   //CleanEntityList(g_md3Cache);\r
114   if (eclass_bad)\r
115   {\r
116     free(eclass_bad->name);\r
117     free(eclass_bad->comments);\r
118     free(eclass_bad);\r
119     eclass_bad = NULL;\r
120   }\r
121 }\r
122 \r
123 void EClass_InsertSortedList(eclass_t *&pList, eclass_t *e)\r
124 {\r
125         eclass_t        *s;\r
126         \r
127         if (!pList)\r
128         {\r
129                 pList = e;\r
130                 return;\r
131         }\r
132 \r
133 \r
134         s = pList;\r
135         if (stricmp (e->name, s->name) < 0)\r
136         {\r
137                 e->next = s;\r
138                 pList = e;\r
139                 return;\r
140         }\r
141 \r
142         do\r
143         {\r
144                 if (!s->next || stricmp (e->name, s->next->name) < 0)\r
145                 {\r
146                         e->next = s->next;\r
147                         s->next = e;\r
148                         return;\r
149                 }\r
150                 s=s->next;\r
151         } while (1);\r
152 }\r
153 \r
154 /*\r
155 =================\r
156 Eclass_InsertAlphabetized\r
157 =================\r
158 */\r
159 void Eclass_InsertAlphabetized (eclass_t *e)\r
160 {\r
161 #if 1\r
162   EClass_InsertSortedList(eclass, e);\r
163 #else\r
164         eclass_t        *s;\r
165         \r
166         if (!eclass)\r
167         {\r
168                 eclass = e;\r
169                 return;\r
170         }\r
171 \r
172 \r
173         s = eclass;\r
174         if (stricmp (e->name, s->name) < 0)\r
175         {\r
176                 e->next = s;\r
177                 eclass = e;\r
178                 return;\r
179         }\r
180 \r
181         do\r
182         {\r
183                 if (!s->next || stricmp (e->name, s->next->name) < 0)\r
184                 {\r
185                         e->next = s->next;\r
186                         s->next = e;\r
187                         return;\r
188                 }\r
189                 s=s->next;\r
190         } while (1);\r
191 #endif\r
192 }\r
193 \r
194 /*!\r
195 This looks at each eclass_t, if it has a "modelpath" set then it leaves it alone\r
196 if it's not set it checks to see if a file called "sprites/<eclassname>.*" exists, and\r
197 if it does exist then it sets the "modelpath" to "sprites/<eclassname>.spr"\r
198 */\r
199 void Eclass_CreateSpriteModelPaths()\r
200 {\r
201   int Counts[4] = { 0, 0, 0, 0 };\r
202   char filename[512]; // should be big enough, ExtractFileBase doesn't take a buffer size...\r
203   eclass_t *e;\r
204 \r
205   // get a list of all sprite/*>* files in all sprite/ directories\r
206   Sys_Printf("Searching VFS for files in sprites/*.* that match entity names...\n");\r
207   GSList *pFiles = vfsGetFileList("sprites", NULL);\r
208   GSList *pFile;\r
209 \r
210   if (pFiles)\r
211   {\r
212   \r
213     // find an eclass without a modelpath.\r
214     for (e=eclass ; e ; e=e->next)\r
215     {\r
216       Counts[0]++;\r
217       if (e->modelpath)\r
218       {\r
219 #ifdef _DEBUG\r
220         Sys_Printf("Ignoring sprite for entity %s (modelpath: \"%s\")\n",e->name,e->modelpath);\r
221 #endif\r
222         Counts[1]++;\r
223         continue;  // ignore this eclass, it's already got a model\r
224       }\r
225 \r
226       // TODO: remove this check when we can have sprites for non-fixed size entities.\r
227       if (!e->fixedsize)\r
228       {\r
229 #ifdef _DEBUG\r
230         Sys_Printf("Ignoring sprite for non-fixed-size entity %s\n",e->name);\r
231 #endif\r
232         Counts[2]++;\r
233         continue;  // can't have sprites for non-fixed size entities (yet!)\r
234       }\r
235 \r
236 \r
237       Sys_Printf("Searching for sprite for fixed-size entity %s...",e->name);\r
238 \r
239       pFile = pFiles; // point to start of list\r
240 \r
241       // look for a file that has the same name, with any extension.\r
242       bool Found = FALSE;\r
243       while (pFile)\r
244       {\r
245 \r
246         // strip the path/ and the .extension.\r
247         ExtractFileBase((char *)pFile->data,filename);\r
248 \r
249         // does the eclass name match the filename?\r
250         if (stricmp(e->name,filename) == 0)\r
251         {\r
252           // yes, so generate a sprite filename using the all-encompasing .spr extension\r
253           // so that the model wrapper knows the sprite model plugin will be the model\r
254           // plugin used to render it.\r
255           CString strSpriteName;\r
256           strSpriteName.Format("sprites/%s.spr",e->name);\r
257           e->modelpath = strdup(strSpriteName.GetBuffer());\r
258           Sys_Printf("Found! (\"%s\")\n",(char *)pFile->data);\r
259           Counts[3]++;\r
260           Found = TRUE;\r
261         }\r
262         pFile = pFile->next;\r
263       }\r
264 \r
265       if (!Found)\r
266         Sys_Printf("not found\n");\r
267 \r
268     }\r
269 \r
270     vfsClearFileDirList(&pFiles);\r
271   }\r
272   Sys_Printf("%d entities were scanned\n"\r
273              "%d entities that already had models/sprites were ignored\n"\r
274              "%d non-fixed-size entities were ignored\n"\r
275              "%d entities did not have matching sprite files\n"\r
276              "%d entities had sprite files and have been attached\n",\r
277              Counts[0],Counts[1],Counts[2],Counts[0]-Counts[3],Counts[3]);\r
278 \r
279 }\r
280 \r
281 void EClass_InitForFileList(GSList *pFiles, _EClassTable *pTable)\r
282 {\r
283   GSList *pFile = pFiles;\r
284   while (pFile)\r
285   {\r
286     // for a given name, we grab the first .def in the vfs\r
287     // this allows to override baseq3/scripts/entities.def for instance\r
288     char relPath[PATH_MAX];\r
289     strcpy(relPath, "scripts/");\r
290     strcat(relPath, (char*)pFile->data);\r
291     // FIXME TTimo http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130\r
292     if (!vfsGetFullPath(relPath, 0, 0))\r
293     {\r
294       Sys_FPrintf(SYS_ERR, "Failed to find the full path for '%s' in the VFS\n", relPath);\r
295       Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n");\r
296     }\r
297     else\r
298       pTable->m_pfnScanFile(vfsGetFullPath(relPath, 0, 0));\r
299     pFile = pFile->next;\r
300   }\r
301 }\r
302 \r
303 /*!\r
304 Manually create an eclass_t, for when no modules exist.\r
305 this replaces and centralizes the eclass_t allocation\r
306 */\r
307 eclass_t * EClass_Create( const char *name, float col1, float col2, float col3, const vec3_t *mins, const vec3_t *maxs, const char *comments )\r
308 {\r
309   eclass_t *e;\r
310         char    color[128];\r
311 \r
312   e = (eclass_t*)malloc(sizeof(*e));\r
313         memset (e, 0, sizeof(*e));\r
314 \r
315   e->name = strdup(name);\r
316 \r
317   // grab the color, reformat as texture name\r
318         e->color[0] = col1;\r
319   e->color[1] = col2;\r
320   e->color[2] = col3;\r
321         sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);\r
322         e->texdef.SetName(color);\r
323 \r
324   // supplied size ?\r
325   if (mins && maxs)\r
326   {\r
327     // Hydra:\r
328     // If we set worldspawn to be a fixed-size all the textures are\r
329     // displayed as flat-shaded.  This is a KLUDGE now that we have\r
330     // multiple game support as the worldspawn entity is game specific.\r
331     // Note that this is only ever fixed for the user if a definition\r
332     // for the worldspawn entity was not loaded, this can happen for\r
333     // several reasons:\r
334     // a) no entity definition plugin exists\r
335     // b) no entity definition files were found\r
336     // c) no entity definition file contained an entry for worldspawn.\r
337 \r
338     if (stricmp(name,"worldspawn") != 0) e->fixedsize = true;\r
339 \r
340     // copy the sizes..\r
341     memcpy(e->mins,mins,sizeof(vec3_t));\r
342     memcpy(e->maxs,maxs,sizeof(vec3_t));\r
343   }\r
344 \r
345   if (comments)\r
346     e->comments = strdup(comments);\r
347   else\r
348   {\r
349     e->comments = (char*)malloc(1);\r
350     e->comments[0] = '\0';\r
351   }\r
352 \r
353   return e;\r
354 }\r
355 \r
356 void Eclass_Init ()\r
357 {\r
358   GSList *pFiles;\r
359 \r
360   // start by creating the default unknown eclass\r
361   eclass_bad = EClass_Create("UNKNOWN_CLASS" , 0, 0.5, 0,NULL,NULL,NULL);\r
362   \r
363   // now scan the definitions\r
364   _EClassTable *pTable = &g_EClassDefTable;\r
365   while (pTable)\r
366   {\r
367     // read in all scripts/*.<extension>\r
368     pFiles = vfsGetFileList("scripts", pTable->m_pfnGetExtension());\r
369     if (pFiles)\r
370     {\r
371       GSList *pFile = pFiles;\r
372       while (pFile)\r
373       {\r
374         /*!\r
375         \todo the MP/SP filtering rules need to be CLEANED UP and SANITIZED\r
376         */\r
377         // HACK\r
378         // JKII SP/MP mapping mode\r
379         if (g_pGameDescription->mGameFile == "jk2.game" || g_pGameDescription->mGameFile == "ja.game")\r
380         {\r
381           if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"), "sp"))\r
382           {\r
383             // SP mapping, ignore mp_*.def\r
384             char *name = (char *)pFile->data;\r
385             if (name[0]=='m' && name[1]=='p' && name[2]=='_')\r
386             {\r
387               Sys_Printf("Single Player mapping mode. Ignoring '%s'\n", name);\r
388               pFile = pFile->next;\r
389               continue;\r
390             }\r
391           }\r
392           else\r
393           {\r
394             // MP mapping, ignore sp_*.def\r
395             char *name = (char *)pFile->data;\r
396             if (name[0]=='s' && name[1]=='p' && name[2]=='_')\r
397             {\r
398               Sys_Printf("Multiplayer mapping mode. Ignoring '%s'\n", name);\r
399               pFile = pFile->next;\r
400               continue;\r
401             }\r
402           }\r
403         }\r
404         // RIANT\r
405         // STVEF SP/MP mapping mode\r
406         else if (g_pGameDescription->mGameFile == "stvef.game")\r
407         {\r
408           if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"), "sp"))\r
409           {\r
410             // SP mapping, ignore mp_*.def\r
411             char *name = (char *)pFile->data;\r
412             if (name[0]=='m' && name[1]=='p' && name[2]=='_')\r
413             {\r
414               Sys_Printf("Single Player mapping mode. Ignoring '%s'\n", name);\r
415               pFile = pFile->next;\r
416               continue;\r
417             }\r
418           }\r
419           else\r
420           {\r
421             // HM mapping, ignore sp_*.def\r
422             char *name = (char *)pFile->data;\r
423             if (name[0]=='h' && name[1]=='m' && name[2]=='_')\r
424             {\r
425               Sys_Printf("HoloMatch mapping mode. Ignoring '%s'\n", name);\r
426               pFile = pFile->next;\r
427               continue;\r
428             }\r
429           }\r
430         }\r
431         // for a given name, we grab the first .def in the vfs\r
432         // this allows to override baseq3/scripts/entities.def for instance\r
433         char relPath[PATH_MAX];\r
434         strcpy(relPath, "scripts/");\r
435         strcat(relPath, (char*)pFile->data);\r
436         // FIXME TTimo http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130\r
437         char *fullpath = vfsGetFullPath(relPath, 0, 0);\r
438         if (!fullpath)\r
439         {\r
440           Sys_FPrintf(SYS_ERR, "Failed to find the full path for \"%s\" in the VFS\n", relPath);\r
441           Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n");\r
442         }\r
443         else\r
444           pTable->m_pfnScanFile(fullpath);\r
445         if (g_pGameDescription->mEClassSingleLoad)\r
446           break;\r
447         pFile = pFile->next;\r
448       }\r
449       vfsClearFileDirList(&pFiles);\r
450       pFiles = NULL;\r
451     }\r
452     else\r
453       Sys_FPrintf(SYS_ERR, "Didn't find any scripts/*.%s files to load EClass information\n", pTable->m_pfnGetExtension());\r
454     \r
455     // we deal with two formats max, if the other table exists, loop again\r
456     if (g_bHaveEClassExt && pTable == &g_EClassDefTable)\r
457       pTable = &g_EClassExtTable;\r
458     else\r
459       pTable = NULL; // done, exit\r
460   }\r
461   Eclass_CreateSpriteModelPaths();\r
462 }\r
463 \r
464 eclass_t *Eclass_ForName (const char *name, qboolean has_brushes)\r
465 {\r
466         eclass_t        *e;\r
467 \r
468   if (!name || *name == '\0')\r
469     return eclass_bad;\r
470 \r
471 #ifdef _DEBUG\r
472   // grouping stuff, not an eclass\r
473   if (strcmp(name, "group_info")==0)\r
474     Sys_Printf("WARNING: unexpected group_info entity in Eclass_ForName\n");\r
475 #endif\r
476 \r
477         if (!name)\r
478                 return eclass_bad;\r
479 \r
480         for (e=eclass ; e ; e=e->next)\r
481                 if (!strcmp (name, e->name))\r
482                         return e;\r
483 \r
484         // create a new class for it\r
485         if (has_brushes)\r
486         {\r
487     e = EClass_Create(name , 0, 0.5, 0,NULL,NULL,"Not found in source.");\r
488         }\r
489         else\r
490         {\r
491     e = EClass_Create(name , 0, 0.5, 0,&smallbox[0],&smallbox[1],"Not found in source.");\r
492         }\r
493 \r
494         Eclass_InsertAlphabetized (e);\r
495 \r
496         return e;\r
497 }\r