allow changing the "Alternate texture projection" mode at runtime (requires Map_New...
[divverent/netradiant.git] / radiant / eclass_def.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
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.
11
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.
16
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
20 */
21
22 #include "eclass_def.h"
23
24 #include "iscriplib.h"
25 #include "ifilesystem.h"
26 #include "iarchive.h"
27
28 #include "eclasslib.h"
29 #include "stream/stringstream.h"
30 #include "stream/textfilestream.h"
31 #include "modulesystem/moduleregistry.h"
32 #include "os/path.h"
33
34 const char* EClass_GetExtension()
35 {
36   return "def";
37 }
38 void Eclass_ScanFile (EntityClassCollector& collector, const char *filename);
39
40
41 #include "modulesystem/singletonmodule.h"
42
43 class EntityClassDefDependencies : public GlobalShaderCacheModuleRef, public GlobalScripLibModuleRef
44 {
45 };
46
47 class EclassDefAPI
48 {
49   EntityClassScanner m_eclassdef;
50 public:
51   typedef EntityClassScanner Type;
52   STRING_CONSTANT(Name, "def");
53
54   EclassDefAPI()
55   {
56     m_eclassdef.scanFile = &Eclass_ScanFile;
57     m_eclassdef.getExtension = &EClass_GetExtension;
58   }
59   EntityClassScanner* getTable()
60   {
61     return &m_eclassdef;
62   }
63 };
64
65 typedef SingletonModule<EclassDefAPI, EntityClassDefDependencies> EclassDefModule;
66 typedef Static<EclassDefModule> StaticEclassDefModule;
67 StaticRegisterModule staticRegisterEclassDef(StaticEclassDefModule::instance());
68
69
70 #include "string/string.h"
71
72 #include <stdlib.h>
73
74
75 char            com_token[1024];
76 bool    com_eof;
77
78 /*
79 ==============
80 COM_Parse
81
82 Parse a token out of a string
83 ==============
84 */
85 const char *COM_Parse (const char *data)
86 {
87         int             c;
88         int             len;
89
90         len = 0;
91         com_token[0] = 0;
92         
93         if (!data)
94                 return 0;
95                 
96 // skip whitespace
97 skipwhite:
98         while ( (c = *data) <= ' ')
99         {
100                 if (c == 0)
101                 {
102                         com_eof = true;
103                         return 0;                       // end of file;
104                 }
105                 data++;
106         }
107         
108 // skip // comments
109         if (c=='/' && data[1] == '/')
110         {
111                 while (*data && *data != '\n')
112                         data++;
113                 goto skipwhite;
114         }
115         
116
117 // handle quoted strings specially
118         if (c == '\"')
119         {
120                 data++;
121                 do
122                 {
123                         c = *data++;
124                         if (c=='\"')
125                         {
126                                 com_token[len] = 0;
127                                 return data;
128                         }
129                         com_token[len] = c;
130                         len++;
131                 } while (1);
132         }
133
134 // parse single characters
135         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
136         {
137                 com_token[len] = c;
138                 len++;
139                 com_token[len] = 0;
140                 return data+1;
141         }
142
143 // parse a regular word
144         do
145         {
146                 com_token[len] = c;
147                 data++;
148                 len++;
149                 c = *data;
150         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
151                         break;
152         } while (c>32);
153         
154         com_token[len] = 0;
155         return data;
156 }
157
158 const char* Get_COM_Token()
159 {
160   return com_token;
161 }
162
163
164 const char *debugname;
165
166 void setSpecialLoad(EntityClass *e, const char* pWhat, CopiedString& p)
167 {
168   // Hydra: removed some amazingly bad cstring usage, whoever wrote that
169   // needs to be taken out and shot.
170
171   const char *pText = 0;
172   const char *where = 0;
173
174   where = strstr(e->comments(),pWhat);
175   if (!where)
176     return;
177
178   pText = where + strlen(pWhat);
179   if (*pText == '\"')
180     pText++;
181
182   where = strchr(pText,'\"');
183   if (where)
184   {
185     p = StringRange(pText, where);
186   }
187   else
188   {
189     p = pText;
190   }
191 }
192
193 #include "eclasslib.h"
194
195 /*
196
197 the classname, color triple, and bounding box are parsed out of comments
198 A ? size means take the exact brush size.
199
200 / *QUAKED <classname> (0 0 0) ?
201 / *QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)
202
203 Flag names can follow the size description:
204
205 / *QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY
206
207 */
208
209 EntityClass *Eclass_InitFromText (const char *text)
210 {
211         EntityClass* e = Eclass_Alloc();
212   e->free = &Eclass_Free;
213
214   // grab the name
215         text = COM_Parse (text);
216         e->m_name = Get_COM_Token();
217         debugname = e->name();
218
219   {
220     // grab the color, reformat as texture name
221     int r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]);
222     if (r != 3)
223                   return e;
224           eclass_capture_state(e);
225   }
226
227         while (*text != ')')
228   {
229     if (!*text) {
230                         return 0;
231     }
232                 text++;
233         }
234         text++;
235         
236   // get the size       
237         text = COM_Parse (text);
238         if (Get_COM_Token()[0] == '(')
239         {       // parse the size as two vectors
240                 e->fixedsize = true;
241                 int r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2],
242                         &e->maxs[0], &e->maxs[1], &e->maxs[2]);
243     if (r != 6) {
244                         return 0;
245     }
246
247                 for (int i=0 ; i<2 ; i++)
248                 {
249                         while (*text != ')')
250                         {
251         if (!*text) {
252                                         return 0;
253         }
254                                 text++;
255                         }
256                         text++;
257                 }
258         }
259         
260         char    parms[256];
261   // get the flags
262   {
263     // copy to the first /n
264           char* p = parms;
265           while (*text && *text != '\n')
266                   *p++ = *text++;
267           *p = 0;
268           text++;
269   }
270         
271   {
272     // any remaining words are parm flags
273           const char* p = parms;
274           for (std::size_t i=0 ; i<MAX_FLAGS ; i++)
275           {
276                   p = COM_Parse (p);
277                   if (!p)
278                           break;
279                   strcpy (e->flagnames[i], Get_COM_Token());
280           }
281   }
282
283         e->m_comments = text;
284
285   setSpecialLoad(e, "model=", e->m_modelpath);
286   StringOutputStream buffer(string_length(e->m_modelpath.c_str()));
287   buffer << PathCleaned(e->m_modelpath.c_str());
288   e->m_modelpath = buffer.c_str();
289
290   if(!e->fixedsize)
291   {
292     EntityClass_insertAttribute(*e, "angle", EntityClassAttribute("direction", "Direction", "0"));
293   }
294   else
295   {
296     EntityClass_insertAttribute(*e, "angle", EntityClassAttribute("angle", "Yaw Angle", "0"));
297   }
298   EntityClass_insertAttribute(*e, "model", EntityClassAttribute("model", "Model"));
299   EntityClass_insertAttribute(*e, "noise", EntityClassAttribute("sound", "Sound"));
300
301   return e;
302 }
303
304 void Eclass_ScanFile (EntityClassCollector& collector, const char *filename)
305 {
306         EntityClass     *e;
307         
308   TextFileInputStream inputFile(filename);
309   if(inputFile.failed())
310   {
311     globalErrorStream() << "ScanFile: " << filename << " not found\n";
312     return;
313   }
314   globalOutputStream() << "ScanFile: " << filename << "\n";
315
316   enum EParserState
317   {
318     eParseDefault,
319     eParseSolidus,
320     eParseComment,
321     eParseQuakeED,
322     eParseEntityClass,
323     eParseEntityClassEnd,
324   } state = eParseDefault;
325   const char* quakeEd = "QUAKED";
326   const char* p = 0;
327   StringBuffer buffer;
328   SingleCharacterInputStream<TextFileInputStream> bufferedInput(inputFile);
329   for(;;)
330   {
331     char c;
332     if(!bufferedInput.readChar(c))
333     {
334       break;
335     }
336
337     switch(state)
338     {
339     case eParseDefault:
340       if(c == '/')
341       {
342         state = eParseSolidus;
343       }
344       break;
345     case eParseSolidus:
346       if(c == '/')
347       {
348         state = eParseComment;
349       }
350       else if(c == '*')
351       {
352         p = quakeEd;
353         state = eParseQuakeED;
354       }
355       break;
356     case eParseComment:
357       if(c == '\n')
358       {
359         state = eParseDefault;
360       }
361       break;
362     case eParseQuakeED:
363       if(c == *p)
364       {
365         if(*(++p) == '\0')
366         {
367           state = eParseEntityClass;
368         }
369       }
370       else
371       {
372         state = eParseDefault;
373       }
374       break;
375     case eParseEntityClass:
376       if(c == '*')
377       {
378         state = eParseEntityClassEnd;
379       }
380       else
381       {
382         buffer.push_back(c);
383       }
384       break;
385     case eParseEntityClassEnd:
386       if(c == '/')
387       {
388         e = Eclass_InitFromText(buffer.c_str());
389         state = eParseDefault;
390         if (e)
391           collector.insert(e);
392         else
393           globalErrorStream() << "Error parsing: " << debugname << " in " << filename << "\n";
394
395         buffer.clear();
396         state = eParseDefault;
397       }
398       else
399       {
400         buffer.push_back('*');
401         buffer.push_back(c);
402         state = eParseEntityClass;
403       }
404       break;
405     }
406   }
407 }