]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/map/parse.cpp
error check and bail if permission denied during gamepack install
[xonotic/netradiant.git] / plugins / map / parse.cpp
1 /*
2    Copyright (C) 1999-2007 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 // Hydra - FIXME : TTimo, We need to know what game + engine we're using for
23 // the halflife (not Q2) specific stuff
24 // we need an API for modules to get this info!
25
26 //
27 // parses quake3 map format into internal objects
28 //
29
30 #include "plugin.h"
31
32 // cmdlib
33 extern void ExtractFileName( const char *path, char *dest );
34
35 extern int g_MapVersion;
36 int abortcode; // see imap.h for values.
37
38 // Start of half-life specific stuff
39
40 GSList *g_WadList; // halflife specific.
41 GSList *g_TextureNameCache; // halflife specific.
42
43 // NOTE TTimo: yuck..
44 void FreeGSList( GSList *l ){
45         while ( l )
46         {
47                 free( l->data );
48                 l = g_slist_remove( l, l->data );
49         }
50 }
51
52 // NOTE TTimo: ideally, this would be using Str functions instead
53 void trim( char *str ){
54         int len;
55         len = strlen( str );
56         while ( str[--len] == ' ' )
57                 str[len] = 0;
58 }
59
60 void BuildWadList( char *wadstr ){
61         char wads[2048]; // change to CString usage ?
62         wads[0] = 0;
63         char *p1,*p2;
64         char cleanwadname[QER_MAX_NAMELEN];
65
66         g_WadList = NULL;
67
68         strcpy( wads,wadstr );
69         QE_ConvertDOSToUnixName( wads,wads );
70
71         // ok, we got the list of ; delimited wads, now split it into a GSList that contains
72         // just the wad names themselves.
73
74         p1 = wads;
75
76         do
77         {
78                 p2 = strchr( p1,';' );
79                 if ( p2 ) {
80                         *p2 = 0; // swap the ; with a null terminator
81
82                 }
83                 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
84                         ExtractFileName( p1,cleanwadname );
85
86                         trim( cleanwadname );
87
88                         if ( *cleanwadname ) {
89                                 g_WadList = g_slist_append( g_WadList, strdup( cleanwadname ) );
90                                 Sys_Printf( "wad: %s\n",cleanwadname );
91                         }
92                 }
93                 else
94                 {
95                         trim( p1 );
96                         if ( *p1 ) {
97                                 g_WadList = g_slist_append( g_WadList, strdup( p1 ) );
98                                 Sys_Printf( "wad: %s\n",p1 );
99                         }
100                 }
101                 if ( p2 ) {
102                         p1 = p2 + 1; // point back to the remainder of the string
103                 }
104                 else{
105                         p1 = NULL; // make it so we exit the loop.
106
107                 }
108         } while ( p1 );
109
110         // strip the ".wad" extensions.
111         for ( GSList *l = g_WadList; l != NULL ; l = l->next )
112         {
113                 p1 = (char *)l->data;
114
115                 if ( p1[strlen( p1 ) - 4] == '.' ) {
116                         p1[strlen( p1 ) - 4] = 0;
117                 }
118         }
119 }
120
121 // FIXME: usefulness of this cache sounds very discutable
122 char *CheckCacheForTextureName( const char *cleantexturename ){
123         char *str;
124         int len;
125         GSList *l;
126
127         // search our little cache first to speed things up.
128         // cache strings are stored as "<cleanname>;<actualnameshader>"
129         len = strlen( cleantexturename );
130         for ( l = g_TextureNameCache; l != NULL ; l = l->next )
131         {
132                 str = (char *)l->data;
133                 if ( ( strnicmp( cleantexturename,str,len ) == 0 ) && ( str[len] == ';' ) ) { // must do in this order or we'll get an access violation, even though it's slower.
134                         return ( str + len + 1 ); // skip the delimiter ;
135                 }
136         }
137         return NULL;
138 }
139
140 char *AddToCache( const char *cleantexturename, const char *actualname ){
141         char *cachestr;
142         cachestr = (char *)malloc( strlen( cleantexturename ) + 1 + strlen( actualname ) + 1 ); // free()'d when g_TextureNameCache is freed
143         sprintf( cachestr,"%s;%s",cleantexturename,actualname );
144         g_TextureNameCache = g_slist_append( g_TextureNameCache, cachestr );
145         return cachestr;
146 }
147
148 char *SearchWadsForTextureName( const char *cleantexturename ){
149         char *str;
150         char *wadname;
151         char *actualtexturename = NULL;
152         GSList *l;
153         int count;
154
155         actualtexturename = CheckCacheForTextureName( cleantexturename );
156         if ( actualtexturename ) {
157                 return actualtexturename;
158         }
159
160         // still here ?  guess it's not in the cache then!
161
162         // search the wads listed in the worldspawn "wad" key
163         for ( l = g_WadList; l != NULL && actualtexturename == NULL ; l = l->next )
164         {
165                 wadname = (char *)l->data;
166
167                 str = new char[strlen( wadname ) + strlen( cleantexturename ) + 9 + 1 + 4 + 1];
168
169                 // hlw here is ok as we never have anything other than hlw files in a wad.
170                 sprintf( str,"textures/%s/%s.hlw",wadname,cleantexturename );
171                 count = vfsGetFileCount( str, VFS_SEARCH_PAK ); // only search pack files
172                 // LordHavoc: hacked in .mip loading here
173                 if ( !count ) {
174                         sprintf( str,"textures/%s/%s.mip",wadname,cleantexturename );
175                         count = vfsGetFileCount( str, VFS_SEARCH_PAK ); // only search pack files
176                 }
177
178                 if ( count > 0 ) {
179                         // strip the extension, build the cache string and add the the cache
180                         str[strlen( str ) - 4] = 0;
181
182                         actualtexturename = AddToCache( cleantexturename,str );
183
184                         //point the return value to the actual name, not what we add to the cache
185                         actualtexturename += 1 + strlen( cleantexturename );
186                 }
187                 delete [] str;
188         }
189         return actualtexturename;
190 }
191 // End of half-life specific stuff
192
193 void Patch_Parse( patchMesh_t *pPatch ){
194         int i, j;
195         char *str;
196
197         char *token = Token();
198
199         GetToken( true ); //{
200
201         // parse shader name
202         GetToken( true );
203         str = new char[strlen( token ) + 10];
204         strcpy( str, "textures/" );
205         strcpy( str + 9, token );
206         pPatch->pShader = QERApp_Shader_ForName( str );
207         pPatch->d_texture = pPatch->pShader->getTexture();
208         delete [] str;
209
210         GetToken( true ); //(
211
212         // parse matrix dimensions
213         GetToken( false );
214         pPatch->width = atoi( token );
215         GetToken( false );
216         pPatch->height = atoi( token );
217
218         // ignore contents/flags/value
219         GetToken( false );
220         GetToken( false );
221         GetToken( false );
222
223         GetToken( false ); //)
224
225         // parse matrix
226         GetToken( true ); //(
227         for ( i = 0; i < pPatch->width; i++ )
228         {
229                 GetToken( true ); //(
230                 for ( j = 0; j < pPatch->height; j++ )
231                 {
232                         GetToken( false ); //(
233
234                         GetToken( false );
235                         pPatch->ctrl[i][j].xyz[0] = atof( token );
236                         GetToken( false );
237                         pPatch->ctrl[i][j].xyz[1] = atof( token );
238                         GetToken( false );
239                         pPatch->ctrl[i][j].xyz[2] = atof( token );
240                         GetToken( false );
241                         pPatch->ctrl[i][j].st[0] = atof( token );
242                         GetToken( false );
243                         pPatch->ctrl[i][j].st[1] = atof( token );
244
245                         GetToken( false ); //)
246                 }
247                 GetToken( false ); //)
248         }
249         GetToken( true ); //)
250
251         GetToken( true ); //}
252 }
253
254 void Face_Parse( face_t *face, bool bAlternateTexdef = false ){
255         int i, j;
256         char *str;
257         bool bworldcraft = false;
258
259         char *token = Token();
260
261         // parse planepts
262         str = NULL;
263         for ( i = 0; i < 3; i++ )
264         {
265                 GetToken( true ); //(
266                 for ( j = 0; j < 3; j++ )
267                 {
268                         GetToken( false );
269                         face->planepts[i][j] = atof( token );
270                 }
271                 GetToken( false ); //)
272         }
273
274         if ( bAlternateTexdef ) {
275                 // parse alternate texdef
276                 GetToken( false ); // (
277                 GetToken( false ); // (
278                 for ( i = 0; i < 3; i++ )
279                 {
280                         GetToken( false );
281                         face->brushprimit_texdef.coords[0][i] = atof( token );
282                 }
283                 GetToken( false ); // )
284                 GetToken( false ); // (
285                 for ( i = 0; i < 3; i++ )
286                 {
287                         GetToken( false );
288                         face->brushprimit_texdef.coords[1][i] = atof( token );
289                 }
290                 GetToken( false ); // )
291                 GetToken( false ); // )
292         }
293
294
295         // parse shader name
296         GetToken( false ); // shader
297
298         // if we're loading a halflife map then we don't have a relative texture name
299         // we just get <texturename>.  So we need to convert this to a relative name
300         // like this: "textures/<wadname>/shader", so we use vfsFileFile to get the filename.
301
302         // *** IMPORTANT ***
303         // For Halflife we need to see if the texture is in wads listed in the
304         // map's worldspawn "wad" e-pair.  If we don't then the image used will be the
305         // first image with this texture name that is found in any of the wads on the
306         // user's system.  this is not a huge problem, because the map compiler obeys
307         // the "wad" epair when compiling the map, but the user might end up looking at
308         // the wrong texture in the editor. (more of a problem if the texture we use
309         // here has a different size from the one in the wad the map compiler uses...)
310
311         // Hydra: - TTimo: I looked all over for other places to put this, but really it
312         // is an issue with map loading (because of a limitation of halflife/q2 map format)
313         // so it's gone in here, it also stops incorrect shader/texdef names getting used
314         // in the radiant core and it's modules which we'd only have to change later on.
315         // (either in map_importentities() or the shader module).  so it's actually cleaner
316         // in the long run, even if a little odd.  And it keeps more game specific stuff
317         // OUT of the core, which is a good thing.
318
319         if ( g_MapVersion == MAPVERSION_HL ) {
320                 qboolean done = false;
321
322                 // FIXME: This bit is halflife specific.
323                 // look in the list of wads supplied in the worldspawn "wad" key/pair for the
324                 // texture first, if it's not in any then we carry on searching the vfs for it
325                 // as usual.
326
327                 // each time we find a texture, we add it to the a cache
328                 // so we don't have to hunt the vfs for it each time.
329                 // See SearchWadsForTextureName() and AddToCache() above for cache stuff
330
331                 char *wadname;
332                 wadname = SearchWadsForTextureName( token );
333
334                 if ( wadname ) {
335                         face->texdef.SetName( wadname );
336                         done = true;
337                 }
338                 else
339                 {
340                         // using the cache below means that this message is only ever printed out once!
341                         Sys_Printf( "WARNING: could not find \"%s\" in any listed wad files, searching all wad files instead!\n",token );
342                 }
343                 // end of half-life specific bit.
344
345                 // check the cache!
346                 if ( !done ) {
347                         str = CheckCacheForTextureName( token );
348                         if ( str ) {
349                                 face->texdef.SetName( str );
350                                 done = true;
351                         }
352                 }
353
354                 if ( !done ) {
355                         char *fullpath;
356
357                         str = new char[strlen( token ) + 4 + 1];
358
359                         // FIXME: halflife specific file extension, we'll have to support Q2/Q1 formats
360                         // and maybe tga texture format for HL here too..
361                         sprintf( str,"%s.hlw",token );
362                         fullpath = vfsGetFullPath( str,0,VFS_SEARCH_PAK | VFS_SEARCH_DIR );
363
364                         // MIP support for quake
365                         if ( !fullpath ) {
366                                 sprintf( str,"%s.mip",token );
367                                 fullpath = vfsGetFullPath( str, 0, 0 );
368                         }
369
370                         // TGA support in halflife ?
371                         /*
372                            if (!fullpath)
373                            {
374                            sprintf(str,"%s.tga",token);
375                            fullpath = vfsGetFullPath(str);
376                            }
377                          */
378                         delete [] str;
379
380                         if ( fullpath ) {
381                                 // strip the extension.
382                                 int len = strlen( fullpath );
383                                 if ( fullpath[len - 4] == '.' ) {
384                                         fullpath[len - 4] = '\0';
385                                 }
386
387                                 // and set the correct name!
388                                 face->texdef.SetName( fullpath );
389                                 AddToCache( token,fullpath );
390                         }
391                         else
392                         {
393                                 Sys_Printf( "WARNING: could not find \"%s\" in the vfs search path\n",token );
394                                 str = new char[strlen( token ) + 10];
395                                 strcpy( str, "textures/" );
396                                 strcpy( str + 9, token );
397                                 face->texdef.SetName( str );
398                                 AddToCache( token,str );
399                                 delete [] str;
400                         }
401                 }
402         }
403         else // !MAPVERSION_HL
404         {
405                 str = new char[strlen( token ) + 10];
406                 strcpy( str, "textures/" );
407                 strcpy( str + 9, token );
408                 face->texdef.SetName( str );
409                 delete [] str;
410         }
411
412         if ( !bAlternateTexdef ) {
413                 if ( g_MapVersion == MAPVERSION_HL ) { // Q1 as well ?
414                         GetToken( false );
415                         if ( token[0] == '[' && token[1] == '\0' ) {
416                                 bworldcraft = true;
417
418                                 GetToken( false ); // UAxis[0]
419                                 GetToken( false ); // UAxis[1]
420                                 GetToken( false ); // UAxis[2]
421
422                                 GetToken( false ); // shift
423                                 face->texdef.shift[0] = atof( token );
424
425                                 GetToken( false ); // ]
426
427                                 GetToken( false ); // [
428                                 GetToken( false ); // VAxis[0]
429                                 GetToken( false ); // VAxis[1]
430                                 GetToken( false ); // VAxis[2]
431
432                                 GetToken( false ); // shift
433                                 face->texdef.shift[1] = atof( token );
434
435                                 GetToken( false ); // ]
436
437                                 // rotation is derived from the U and V axes.
438                                 // ZHLT ignores this setting even if present in a .map file.
439                                 GetToken( false );
440                                 face->texdef.rotate = atof( token );
441
442                                 // Scales
443                                 GetToken( false );
444                                 face->texdef.scale[0] = atof( token );
445                                 GetToken( false );
446                                 face->texdef.scale[1] = atof( token );
447                         }
448                         else
449                         {
450                                 UnGetToken();
451                         }
452                 }
453
454                 if ( !bworldcraft ) { // !MAPVERSION_HL
455                         // parse texdef
456                         GetToken( false );
457                         face->texdef.shift[0] = atof( token );
458                         GetToken( false );
459                         face->texdef.shift[1] = atof( token );
460                         GetToken( false );
461                         face->texdef.rotate = atof( token );
462                         GetToken( false );
463                         face->texdef.scale[0] = atof( token );
464                         GetToken( false );
465                         face->texdef.scale[1] = atof( token );
466                 }
467         }
468         // parse the optional contents/flags/value
469         if ( !bworldcraft && TokenAvailable() ) {
470                 GetToken( true );
471                 if ( isdigit( token[0] ) ) {
472                         face->texdef.contents = atoi( token );
473                         GetToken( false );
474                         face->texdef.flags = atoi( token );
475                         GetToken( false );
476                         face->texdef.value = atoi( token );
477                 }
478                 else
479                 {
480                         UnGetToken();
481                 }
482         }
483 }
484
485 bool Primitive_Parse( brush_t *pBrush ){
486         char *token = Token();
487
488         GetToken( true );
489         if ( !strcmp( token, "patchDef2" ) ) {
490                 pBrush->patchBrush = true;
491                 pBrush->pPatch = Patch_Alloc();
492                 pBrush->pPatch->pSymbiot = pBrush;
493                 Patch_Parse( pBrush->pPatch );
494                 GetToken( true ); //}
495
496                 // A patchdef should never be loaded from a quake2 map file
497                 // so we just return false and the brush+patch gets freed
498                 // and the user gets told.
499                 if ( g_MapVersion != MAPVERSION_Q3 ) {
500                         // FIXME: Hydra - I wanted to write out a line number here, but I can't because there's no API to access the core's "scriptline" variable.
501                         Syn_Printf( "ERROR: patchDef2's are not supported in Quake%d format .map files!\n",g_MapVersion );
502                         abortcode = MAP_WRONGVERSION;
503                         return false;
504                 }
505         }
506         else if ( !strcmp( token, "brushDef" ) ) {
507                 pBrush->bBrushDef = true;
508                 GetToken( true ); // {
509                 while ( 1 )
510                 {
511                         face_t    *f = pBrush->brush_faces;
512                         pBrush->brush_faces = Face_Alloc();
513                         Face_Parse( pBrush->brush_faces, true );
514                         pBrush->brush_faces->next = f;
515                         // check for end of brush
516                         GetToken( true );
517                         if ( strcmp( token,"}" ) == 0 ) {
518                                 break;
519                         }
520                         UnGetToken();
521                 }
522                 GetToken( true ); // }
523         }
524         else
525         {
526                 UnGetToken();
527                 while ( 1 )
528                 {
529                         face_t    *f = pBrush->brush_faces;
530                         pBrush->brush_faces = Face_Alloc();
531                         Face_Parse( pBrush->brush_faces );
532                         pBrush->brush_faces->next = f;
533
534                         // check for end of brush
535                         GetToken( true );
536                         if ( strcmp( token,"}" ) == 0 ) {
537                                 break;
538                         }
539                         UnGetToken();
540                 }
541         }
542         return true;
543 }
544
545 void Entity_Parse( entity_t *pEntity ){
546         brush_t *pBrush;
547 //  CPtrArray *brushes = NULL;
548         char temptoken[1024];
549
550         char *token = Token();
551
552         while ( 1 )
553         {
554                 GetToken( true ); // { or } or epair
555                 if ( !strcmp( token, "}" ) ) {
556                         break;
557                 }
558                 else if ( !strcmp( token, "{" ) ) {
559
560                         pBrush = Brush_Alloc();
561                         if ( Primitive_Parse( pBrush ) ) {
562                                 ( (CPtrArray*)pEntity->pData )->Add( pBrush );
563                         }
564                         else {
565                                 Brush_Free( pBrush, true );
566                         }
567
568                 }
569                 else {
570
571                         strcpy( temptoken, token );
572                         GetToken( false );
573
574                         SetKeyValue( pEntity, temptoken, token );
575
576                         if ( g_MapVersion == MAPVERSION_HL ) {
577                                 // if we've not god a "wads" key/pair already, then break it into a list.
578                                 if ( !g_WadList && ( stricmp( temptoken,"wad" ) == 0 ) ) {
579                                         BuildWadList( token );
580                                 }
581                         }
582
583                 }
584         }
585 }
586
587 void Map_Read( IDataStream *in, CPtrArray *map ){
588         entity_t *pEntity;
589         char *buf;
590
591         unsigned long len = in->GetLength();
592         buf = new char[len + 1];
593         in->Read( buf, len );
594         buf[len] = '\0';
595         StartTokenParsing( buf );
596         abortcode = MAP_NOERROR;
597
598         while ( abortcode == MAP_NOERROR )
599         {
600                 if ( !GetToken( true ) ) { // { or NULL
601                         break;
602                 }
603                 pEntity = Entity_Alloc();
604                 pEntity->pData = new CPtrArray;
605                 Entity_Parse( pEntity );
606                 map->Add( pEntity );
607         }
608
609         delete [] buf;
610
611         if ( abortcode != MAP_NOERROR ) {
612                 int num_ents, num_brushes,i,j;
613                 entity_t *e;
614                 CPtrArray *brushes;
615
616                 num_ents = map->GetSize();
617                 for ( i = 0; i < num_ents; i++ )
618                 {
619                         e = (entity_t*)map->GetAt( i );
620                         brushes = (CPtrArray*)e->pData;
621                         num_brushes = brushes->GetSize();
622                         for ( j = 0; j < num_brushes; j++ )
623                         {
624                                 Brush_Free( (brush_t *)brushes->GetAt( j ), true );
625                         }
626                         brushes->RemoveAll();
627                         delete brushes;
628                         Entity_Free( e );
629                 }
630                 map->RemoveAll();
631         }
632 }
633
634 void Map_ReadQ3( IDataStream *in, CPtrArray *map ){
635         g_MapVersion = MAPVERSION_Q3;
636         Map_Read( in,map );
637 }
638
639 void Map_ReadHL( IDataStream *in, CPtrArray *map ){
640         g_WadList = NULL;
641         g_TextureNameCache = NULL;
642
643         g_MapVersion = MAPVERSION_HL;
644         Map_Read( in,map );
645
646         FreeGSList( g_TextureNameCache );
647         FreeGSList( g_WadList );
648 }
649
650 void Map_ReadQ2( IDataStream *in, CPtrArray *map ){
651         g_MapVersion = MAPVERSION_Q2;
652         Map_Read( in,map );
653 }