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