2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
22 // Hydra - FIXME : TTimo, We need to know what game + engine we're using for
\r
23 // the halflife (not Q2) specific stuff
\r
24 // we need an API for modules to get this info!
\r
27 // parses quake3 map format into internal objects
\r
33 extern void ExtractFileName (const char *path, char *dest);
\r
35 extern int g_MapVersion;
\r
36 int abortcode; // see imap.h for values.
\r
38 // Start of half-life specific stuff
\r
40 GSList *g_WadList; // halflife specific.
\r
41 GSList *g_TextureNameCache; // halflife specific.
\r
43 // NOTE TTimo: yuck..
\r
44 void FreeGSList( GSList *l )
\r
49 l = g_slist_remove (l, l->data);
\r
53 // NOTE TTimo: ideally, this would be using Str functions instead
\r
54 void trim( char *str)
\r
58 while(str[--len] == ' ')
\r
62 void BuildWadList( char *wadstr )
\r
64 char wads[2048]; // change to CString usage ?
\r
67 char cleanwadname[QER_MAX_NAMELEN];
\r
71 strcpy(wads,wadstr);
\r
72 QE_ConvertDOSToUnixName(wads,wads);
\r
74 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
\r
75 // just the wad names themselves.
\r
81 p2 = strchr(p1,';');
\r
83 *p2 = 0; // swap the ; with a null terminator
\r
85 if (strchr(p1,'/') || strchr(p1,'\\'))
\r
87 ExtractFileName(p1,cleanwadname);
\r
93 g_WadList = g_slist_append (g_WadList, strdup(cleanwadname));
\r
94 Sys_Printf("wad: %s\n",cleanwadname);
\r
102 g_WadList = g_slist_append (g_WadList, strdup(p1));
\r
103 Sys_Printf("wad: %s\n",p1);
\r
107 p1 = p2+1; // point back to the remainder of the string
\r
109 p1 = NULL; // make it so we exit the loop.
\r
113 // strip the ".wad" extensions.
\r
114 for (GSList *l = g_WadList; l != NULL ; l = l->next)
\r
116 p1 = (char *)l->data;
\r
118 if (p1[strlen(p1)-4] == '.')
\r
119 p1[strlen(p1)-4] = 0;
\r
123 // FIXME: usefulness of this cache sounds very discutable
\r
124 char *CheckCacheForTextureName( const char *cleantexturename )
\r
130 // search our little cache first to speed things up.
\r
131 // cache strings are stored as "<cleanname>;<actualnameshader>"
\r
132 len = strlen(cleantexturename);
\r
133 for (l = g_TextureNameCache; l != NULL ; l = l->next)
\r
135 str = (char *)l->data;
\r
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.
\r
138 return (str + len + 1); // skip the delimiter ;
\r
144 char *AddToCache(const char *cleantexturename, const char *actualname)
\r
147 cachestr = (char *)malloc(strlen(cleantexturename)+1+strlen(actualname)+1); // free()'d when g_TextureNameCache is freed
\r
148 sprintf(cachestr,"%s;%s",cleantexturename,actualname);
\r
149 g_TextureNameCache = g_slist_append (g_TextureNameCache, cachestr);
\r
153 char *SearchWadsForTextureName( const char *cleantexturename )
\r
157 char *actualtexturename = NULL;
\r
161 actualtexturename = CheckCacheForTextureName(cleantexturename);
\r
162 if (actualtexturename)
\r
163 return actualtexturename;
\r
165 // still here ? guess it's not in the cache then!
\r
167 // search the wads listed in the worldspawn "wad" key
\r
168 for (l = g_WadList; l != NULL && actualtexturename == NULL ; l = l->next)
\r
170 wadname = (char *)l->data;
\r
172 str = new char[strlen(wadname)+strlen(cleantexturename)+9+1+4+1];
\r
174 // hlw here is ok as we never have anything other than hlw files in a wad.
\r
175 sprintf(str,"textures/%s/%s.hlw",wadname,cleantexturename);
\r
176 count = vfsGetFileCount(str, VFS_SEARCH_PAK); // only search pack files
\r
177 // LordHavoc: hacked in .mip loading here
\r
180 sprintf(str,"textures/%s/%s.mip",wadname,cleantexturename);
\r
181 count = vfsGetFileCount(str, VFS_SEARCH_PAK); // only search pack files
\r
186 // strip the extension, build the cache string and add the the cache
\r
187 str[strlen(str)-4] = 0;
\r
189 actualtexturename = AddToCache(cleantexturename,str);
\r
191 //point the return value to the actual name, not what we add to the cache
\r
192 actualtexturename += 1+strlen(cleantexturename);
\r
196 return actualtexturename;
\r
198 // End of half-life specific stuff
\r
200 void Patch_Parse(patchMesh_t *pPatch)
\r
205 char *token = Token();
\r
207 GetToken(true); //{
\r
209 // parse shader name
\r
211 str = new char[strlen(token)+10];
\r
212 strcpy(str, "textures/");
\r
213 strcpy(str+9, token);
\r
214 pPatch->pShader = QERApp_Shader_ForName(str);
\r
215 pPatch->d_texture = pPatch->pShader->getTexture();
\r
218 GetToken(true); //(
\r
220 // parse matrix dimensions
\r
222 pPatch->width = atoi(token);
\r
224 pPatch->height = atoi(token);
\r
226 // ignore contents/flags/value
\r
231 GetToken(false); //)
\r
234 GetToken(true); //(
\r
235 for(i=0; i<pPatch->width; i++)
\r
237 GetToken(true); //(
\r
238 for(j=0; j<pPatch->height; j++)
\r
240 GetToken(false); //(
\r
243 pPatch->ctrl[i][j].xyz[0] = atof(token);
\r
245 pPatch->ctrl[i][j].xyz[1] = atof(token);
\r
247 pPatch->ctrl[i][j].xyz[2] = atof(token);
\r
249 pPatch->ctrl[i][j].st[0] = atof(token);
\r
251 pPatch->ctrl[i][j].st[1] = atof(token);
\r
253 GetToken(false); //)
\r
255 GetToken(false); //)
\r
257 GetToken(true); //)
\r
259 GetToken(true); //}
\r
262 void Face_Parse (face_t *face, bool bAlternateTexdef = false)
\r
266 bool bworldcraft = false;
\r
268 char *token = Token();
\r
274 GetToken(true); //(
\r
278 face->planepts[i][j] = atof(token);
\r
280 GetToken(false); //)
\r
283 if(bAlternateTexdef)
\r
285 // parse alternate texdef
\r
286 GetToken (false); // (
\r
287 GetToken (false); // (
\r
291 face->brushprimit_texdef.coords[0][i]=atof(token);
\r
293 GetToken (false); // )
\r
294 GetToken (false); // (
\r
298 face->brushprimit_texdef.coords[1][i]=atof(token);
\r
300 GetToken (false); // )
\r
301 GetToken (false); // )
\r
305 // parse shader name
\r
306 GetToken(false); // shader
\r
308 // if we're loading a halflife map then we don't have a relative texture name
\r
309 // we just get <texturename>. So we need to convert this to a relative name
\r
310 // like this: "textures/<wadname>/shader", so we use vfsFileFile to get the filename.
\r
312 // *** IMPORTANT ***
\r
313 // For Halflife we need to see if the texture is in wads listed in the
\r
314 // map's worldspawn "wad" e-pair. If we don't then the image used will be the
\r
315 // first image with this texture name that is found in any of the wads on the
\r
316 // user's system. this is not a huge problem, because the map compiler obeys
\r
317 // the "wad" epair when compiling the map, but the user might end up looking at
\r
318 // the wrong texture in the editor. (more of a problem if the texture we use
\r
319 // here has a different size from the one in the wad the map compiler uses...)
\r
321 // Hydra: - TTimo: I looked all over for other places to put this, but really it
\r
322 // is an issue with map loading (because of a limitation of halflife/q2 map format)
\r
323 // so it's gone in here, it also stops incorrect shader/texdef names getting used
\r
324 // in the radiant core and it's modules which we'd only have to change later on.
\r
325 // (either in map_importentities() or the shader module). so it's actually cleaner
\r
326 // in the long run, even if a little odd. And it keeps more game specific stuff
\r
327 // OUT of the core, which is a good thing.
\r
329 if (g_MapVersion == MAPVERSION_HL)
\r
331 qboolean done = false;
\r
333 // FIXME: This bit is halflife specific.
\r
334 // look in the list of wads supplied in the worldspawn "wad" key/pair for the
\r
335 // texture first, if it's not in any then we carry on searching the vfs for it
\r
338 // each time we find a texture, we add it to the a cache
\r
339 // so we don't have to hunt the vfs for it each time.
\r
340 // See SearchWadsForTextureName() and AddToCache() above for cache stuff
\r
343 wadname = SearchWadsForTextureName(token);
\r
347 face->texdef.SetName(wadname);
\r
352 // using the cache below means that this message is only ever printed out once!
\r
353 Sys_Printf("WARNING: could not find \"%s\" in any listed wad files, searching all wad files instead!\n",token);
\r
355 // end of half-life specific bit.
\r
357 // check the cache!
\r
360 str = CheckCacheForTextureName(token);
\r
363 face->texdef.SetName(str);
\r
372 str = new char[strlen(token)+4+1];
\r
374 // FIXME: halflife specific file extension, we'll have to support Q2/Q1 formats
\r
375 // and maybe tga texture format for HL here too..
\r
376 sprintf(str,"%s.hlw",token);
\r
377 fullpath = vfsGetFullPath(str,0,VFS_SEARCH_PAK | VFS_SEARCH_DIR);
\r
379 // MIP support for quake
\r
382 sprintf(str,"%s.mip",token);
\r
383 fullpath = vfsGetFullPath( str, 0, 0 );
\r
386 // TGA support in halflife ?
\r
390 sprintf(str,"%s.tga",token);
\r
391 fullpath = vfsGetFullPath(str);
\r
398 // strip the extension.
\r
399 int len = strlen(fullpath);
\r
400 if (fullpath[len-4] == '.')
\r
401 fullpath[len-4] = '\0';
\r
403 // and set the correct name!
\r
404 face->texdef.SetName(fullpath);
\r
405 AddToCache(token,fullpath);
\r
409 Sys_Printf("WARNING: could not find \"%s\" in the vfs search path\n",token);
\r
410 str = new char[strlen(token)+10];
\r
411 strcpy(str, "textures/");
\r
412 strcpy(str+9, token);
\r
413 face->texdef.SetName(str);
\r
414 AddToCache(token,str);
\r
419 else // !MAPVERSION_HL
\r
421 str = new char[strlen(token)+10];
\r
422 strcpy(str, "textures/");
\r
423 strcpy(str+9, token);
\r
424 face->texdef.SetName(str);
\r
428 if(!bAlternateTexdef)
\r
430 if (g_MapVersion == MAPVERSION_HL) // Q1 as well ?
\r
433 if (token[0] == '[' && token[1] == '\0')
\r
435 bworldcraft = true;
\r
437 GetToken(false); // UAxis[0]
\r
438 GetToken(false); // UAxis[1]
\r
439 GetToken(false); // UAxis[2]
\r
441 GetToken(false); // shift
\r
442 face->texdef.shift[0] = atof(token);
\r
444 GetToken(false); // ]
\r
446 GetToken(false); // [
\r
447 GetToken(false); // VAxis[0]
\r
448 GetToken(false); // VAxis[1]
\r
449 GetToken(false); // VAxis[2]
\r
451 GetToken(false); // shift
\r
452 face->texdef.shift[1] = atof(token);
\r
454 GetToken(false); // ]
\r
456 // rotation is derived from the U and V axes.
\r
457 // ZHLT ignores this setting even if present in a .map file.
\r
459 face->texdef.rotate = atof(token);
\r
463 face->texdef.scale[0] = atof(token);
\r
465 face->texdef.scale[1] = atof(token);
\r
473 if (!bworldcraft) // !MAPVERSION_HL
\r
477 face->texdef.shift[0] = atof(token);
\r
479 face->texdef.shift[1] = atof(token);
\r
481 face->texdef.rotate = atof(token);
\r
483 face->texdef.scale[0] = atof(token);
\r
485 face->texdef.scale[1] = atof(token);
\r
488 // parse the optional contents/flags/value
\r
489 if (!bworldcraft && TokenAvailable())
\r
492 if (isdigit(token[0]))
\r
494 face->texdef.contents = atoi(token);
\r
496 face->texdef.flags = atoi(token);
\r
498 face->texdef.value = atoi(token);
\r
507 bool Primitive_Parse(brush_t *pBrush)
\r
509 char *token = Token();
\r
512 if (!strcmp(token, "patchDef2"))
\r
514 pBrush->patchBrush = true;
\r
515 pBrush->pPatch = Patch_Alloc();
\r
516 pBrush->pPatch->pSymbiot = pBrush;
\r
517 Patch_Parse(pBrush->pPatch);
\r
518 GetToken(true); //}
\r
520 // A patchdef should never be loaded from a quake2 map file
\r
521 // so we just return false and the brush+patch gets freed
\r
522 // and the user gets told.
\r
523 if (g_MapVersion != MAPVERSION_Q3)
\r
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.
\r
526 Syn_Printf("ERROR: patchDef2's are not supported in Quake%d format .map files!\n",g_MapVersion);
\r
527 abortcode = MAP_WRONGVERSION;
\r
531 else if (!strcmp(token, "brushDef"))
\r
533 pBrush->bBrushDef = true;
\r
534 GetToken(true); // {
\r
537 face_t *f = pBrush->brush_faces;
\r
538 pBrush->brush_faces = Face_Alloc();
\r
539 Face_Parse(pBrush->brush_faces, true);
\r
540 pBrush->brush_faces->next = f;
\r
541 // check for end of brush
\r
543 if(strcmp(token,"}") == 0)
\r
547 GetToken(true); // }
\r
554 face_t *f = pBrush->brush_faces;
\r
555 pBrush->brush_faces = Face_Alloc();
\r
556 Face_Parse(pBrush->brush_faces);
\r
557 pBrush->brush_faces->next = f;
\r
559 // check for end of brush
\r
561 if(strcmp(token,"}") == 0)
\r
569 void Entity_Parse(entity_t *pEntity)
\r
572 CPtrArray *brushes = NULL;
\r
573 char temptoken[1024];
\r
575 char *token = Token();
\r
579 GetToken(true); // { or } or epair
\r
580 if (!strcmp(token, "}")) {
\r
582 } else if(!strcmp(token, "{")) {
\r
584 pBrush = Brush_Alloc();
\r
585 if (Primitive_Parse(pBrush)) {
\r
586 ((CPtrArray*)pEntity->pData)->Add(pBrush);
\r
588 Brush_Free( pBrush, true );
\r
593 strcpy(temptoken, token);
\r
596 SetKeyValue(pEntity, temptoken, token);
\r
598 if (g_MapVersion == MAPVERSION_HL) {
\r
599 // if we've not god a "wads" key/pair already, then break it into a list.
\r
600 if (!g_WadList && (stricmp(temptoken,"wad") == 0)) {
\r
601 BuildWadList(token);
\r
609 void Map_Read (IDataStream *in, CPtrArray *map)
\r
614 unsigned long len = in->GetLength();
\r
615 buf = new char[len+1];
\r
616 in->Read(buf, len);
\r
618 StartTokenParsing(buf);
\r
619 abortcode = MAP_NOERROR;
\r
621 while(abortcode == MAP_NOERROR)
\r
623 if (!GetToken (true)) // { or NULL
\r
625 pEntity = Entity_Alloc();
\r
626 pEntity->pData = new CPtrArray;
\r
627 Entity_Parse(pEntity);
\r
633 if (abortcode != MAP_NOERROR)
\r
635 int num_ents, num_brushes,i,j;
\r
637 CPtrArray *brushes;
\r
639 num_ents = map->GetSize();
\r
640 for(i=0; i<num_ents; i++)
\r
642 e = (entity_t*)map->GetAt(i);
\r
643 brushes = (CPtrArray*)e->pData;
\r
644 num_brushes = brushes->GetSize();
\r
645 for(j=0; j<num_brushes; j++)
\r
647 Brush_Free( (brush_t *)brushes->GetAt(j), true );
\r
649 brushes->RemoveAll();
\r
657 void Map_ReadQ3 (IDataStream *in, CPtrArray *map)
\r
659 g_MapVersion = MAPVERSION_Q3;
\r
663 void Map_ReadHL (IDataStream *in, CPtrArray *map)
\r
666 g_TextureNameCache = NULL;
\r
668 g_MapVersion = MAPVERSION_HL;
\r
671 FreeGSList(g_TextureNameCache);
\r
672 FreeGSList(g_WadList);
\r
675 void Map_ReadQ2 (IDataStream *in, CPtrArray *map)
\r
677 g_MapVersion = MAPVERSION_Q2;
\r