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