2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
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!
27 // parses quake3 map format into internal objects
33 extern void ExtractFileName (const char *path, char *dest);
35 extern int g_MapVersion;
36 int abortcode; // see imap.h for values.
38 // Start of half-life specific stuff
40 GSList *g_WadList; // halflife specific.
41 GSList *g_TextureNameCache; // halflife specific.
44 void FreeGSList( GSList *l )
49 l = g_slist_remove (l, l->data);
53 // NOTE TTimo: ideally, this would be using Str functions instead
58 while(str[--len] == ' ')
62 void BuildWadList( char *wadstr )
64 char wads[2048]; // change to CString usage ?
67 char cleanwadname[QER_MAX_NAMELEN];
72 QE_ConvertDOSToUnixName(wads,wads);
74 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
75 // just the wad names themselves.
83 *p2 = 0; // swap the ; with a null terminator
85 if (strchr(p1,'/') || strchr(p1,'\\'))
87 ExtractFileName(p1,cleanwadname);
93 g_WadList = g_slist_append (g_WadList, strdup(cleanwadname));
94 Sys_Printf("wad: %s\n",cleanwadname);
102 g_WadList = g_slist_append (g_WadList, strdup(p1));
103 Sys_Printf("wad: %s\n",p1);
107 p1 = p2+1; // point back to the remainder of the string
109 p1 = NULL; // make it so we exit the loop.
113 // strip the ".wad" extensions.
114 for (GSList *l = g_WadList; l != NULL ; l = l->next)
116 p1 = (char *)l->data;
118 if (p1[strlen(p1)-4] == '.')
119 p1[strlen(p1)-4] = 0;
123 // FIXME: usefulness of this cache sounds very discutable
124 char *CheckCacheForTextureName( const char *cleantexturename )
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)
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.
138 return (str + len + 1); // skip the delimiter ;
144 char *AddToCache(const char *cleantexturename, const char *actualname)
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);
153 char *SearchWadsForTextureName( const char *cleantexturename )
157 char *actualtexturename = NULL;
161 actualtexturename = CheckCacheForTextureName(cleantexturename);
162 if (actualtexturename)
163 return actualtexturename;
165 // still here ? guess it's not in the cache then!
167 // search the wads listed in the worldspawn "wad" key
168 for (l = g_WadList; l != NULL && actualtexturename == NULL ; l = l->next)
170 wadname = (char *)l->data;
172 str = new char[strlen(wadname)+strlen(cleantexturename)+9+1+4+1];
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
180 sprintf(str,"textures/%s/%s.mip",wadname,cleantexturename);
181 count = vfsGetFileCount(str, VFS_SEARCH_PAK); // only search pack files
186 // strip the extension, build the cache string and add the the cache
187 str[strlen(str)-4] = 0;
189 actualtexturename = AddToCache(cleantexturename,str);
191 //point the return value to the actual name, not what we add to the cache
192 actualtexturename += 1+strlen(cleantexturename);
196 return actualtexturename;
198 // End of half-life specific stuff
200 void Patch_Parse(patchMesh_t *pPatch)
205 char *token = Token();
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();
220 // parse matrix dimensions
222 pPatch->width = atoi(token);
224 pPatch->height = atoi(token);
226 // ignore contents/flags/value
235 for(i=0; i<pPatch->width; i++)
238 for(j=0; j<pPatch->height; j++)
243 pPatch->ctrl[i][j].xyz[0] = atof(token);
245 pPatch->ctrl[i][j].xyz[1] = atof(token);
247 pPatch->ctrl[i][j].xyz[2] = atof(token);
249 pPatch->ctrl[i][j].st[0] = atof(token);
251 pPatch->ctrl[i][j].st[1] = atof(token);
262 void Face_Parse (face_t *face, bool bAlternateTexdef = false)
266 bool bworldcraft = false;
268 char *token = Token();
278 face->planepts[i][j] = atof(token);
285 // parse alternate texdef
286 GetToken (false); // (
287 GetToken (false); // (
291 face->brushprimit_texdef.coords[0][i]=atof(token);
293 GetToken (false); // )
294 GetToken (false); // (
298 face->brushprimit_texdef.coords[1][i]=atof(token);
300 GetToken (false); // )
301 GetToken (false); // )
306 GetToken(false); // shader
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.
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...)
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.
329 if (g_MapVersion == MAPVERSION_HL)
331 qboolean done = false;
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
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
343 wadname = SearchWadsForTextureName(token);
347 face->texdef.SetName(wadname);
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);
355 // end of half-life specific bit.
360 str = CheckCacheForTextureName(token);
363 face->texdef.SetName(str);
372 str = new char[strlen(token)+4+1];
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);
379 // MIP support for quake
382 sprintf(str,"%s.mip",token);
383 fullpath = vfsGetFullPath( str, 0, 0 );
386 // TGA support in halflife ?
390 sprintf(str,"%s.tga",token);
391 fullpath = vfsGetFullPath(str);
398 // strip the extension.
399 int len = strlen(fullpath);
400 if (fullpath[len-4] == '.')
401 fullpath[len-4] = '\0';
403 // and set the correct name!
404 face->texdef.SetName(fullpath);
405 AddToCache(token,fullpath);
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);
419 else // !MAPVERSION_HL
421 str = new char[strlen(token)+10];
422 strcpy(str, "textures/");
423 strcpy(str+9, token);
424 face->texdef.SetName(str);
428 if(!bAlternateTexdef)
430 if (g_MapVersion == MAPVERSION_HL) // Q1 as well ?
433 if (token[0] == '[' && token[1] == '\0')
437 GetToken(false); // UAxis[0]
438 GetToken(false); // UAxis[1]
439 GetToken(false); // UAxis[2]
441 GetToken(false); // shift
442 face->texdef.shift[0] = atof(token);
444 GetToken(false); // ]
446 GetToken(false); // [
447 GetToken(false); // VAxis[0]
448 GetToken(false); // VAxis[1]
449 GetToken(false); // VAxis[2]
451 GetToken(false); // shift
452 face->texdef.shift[1] = atof(token);
454 GetToken(false); // ]
456 // rotation is derived from the U and V axes.
457 // ZHLT ignores this setting even if present in a .map file.
459 face->texdef.rotate = atof(token);
463 face->texdef.scale[0] = atof(token);
465 face->texdef.scale[1] = atof(token);
473 if (!bworldcraft) // !MAPVERSION_HL
477 face->texdef.shift[0] = atof(token);
479 face->texdef.shift[1] = atof(token);
481 face->texdef.rotate = atof(token);
483 face->texdef.scale[0] = atof(token);
485 face->texdef.scale[1] = atof(token);
488 // parse the optional contents/flags/value
489 if (!bworldcraft && TokenAvailable())
492 if (isdigit(token[0]))
494 face->texdef.contents = atoi(token);
496 face->texdef.flags = atoi(token);
498 face->texdef.value = atoi(token);
507 bool Primitive_Parse(brush_t *pBrush)
509 char *token = Token();
512 if (!strcmp(token, "patchDef2"))
514 pBrush->patchBrush = true;
515 pBrush->pPatch = Patch_Alloc();
516 pBrush->pPatch->pSymbiot = pBrush;
517 Patch_Parse(pBrush->pPatch);
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)
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;
531 else if (!strcmp(token, "brushDef"))
533 pBrush->bBrushDef = true;
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
543 if(strcmp(token,"}") == 0)
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;
559 // check for end of brush
561 if(strcmp(token,"}") == 0)
569 void Entity_Parse(entity_t *pEntity)
572 CPtrArray *brushes = NULL;
573 char temptoken[1024];
575 char *token = Token();
579 GetToken(true); // { or } or epair
580 if (!strcmp(token, "}")) {
582 } else if(!strcmp(token, "{")) {
584 pBrush = Brush_Alloc();
585 if (Primitive_Parse(pBrush)) {
586 ((CPtrArray*)pEntity->pData)->Add(pBrush);
588 Brush_Free( pBrush, true );
593 strcpy(temptoken, token);
596 SetKeyValue(pEntity, temptoken, token);
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)) {
609 void Map_Read (IDataStream *in, CPtrArray *map)
614 unsigned long len = in->GetLength();
615 buf = new char[len+1];
618 StartTokenParsing(buf);
619 abortcode = MAP_NOERROR;
621 while(abortcode == MAP_NOERROR)
623 if (!GetToken (true)) // { or NULL
625 pEntity = Entity_Alloc();
626 pEntity->pData = new CPtrArray;
627 Entity_Parse(pEntity);
633 if (abortcode != MAP_NOERROR)
635 int num_ents, num_brushes,i,j;
639 num_ents = map->GetSize();
640 for(i=0; i<num_ents; i++)
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++)
647 Brush_Free( (brush_t *)brushes->GetAt(j), true );
649 brushes->RemoveAll();
657 void Map_ReadQ3 (IDataStream *in, CPtrArray *map)
659 g_MapVersion = MAPVERSION_Q3;
663 void Map_ReadHL (IDataStream *in, CPtrArray *map)
666 g_TextureNameCache = NULL;
668 g_MapVersion = MAPVERSION_HL;
671 FreeGSList(g_TextureNameCache);
672 FreeGSList(g_WadList);
675 void Map_ReadQ2 (IDataStream *in, CPtrArray *map)
677 g_MapVersion = MAPVERSION_Q2;