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