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