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 ){
48 l = g_slist_remove( l, l->data );
52 // NOTE TTimo: ideally, this would be using Str functions instead
53 void trim( char *str ){
56 while ( str[--len] == ' ' )
60 void BuildWadList( char *wadstr ){
61 char wads[2048]; // change to CString usage ?
64 char cleanwadname[QER_MAX_NAMELEN];
68 strcpy( wads,wadstr );
69 QE_ConvertDOSToUnixName( wads,wads );
71 // ok, we got the list of ; delimited wads, now split it into a GSList that contains
72 // just the wad names themselves.
78 p2 = strchr( p1,';' );
80 *p2 = 0; // swap the ; with a null terminator
83 if ( strchr( p1,'/' ) || strchr( p1,'\\' ) ) {
84 ExtractFileName( p1,cleanwadname );
88 if ( *cleanwadname ) {
89 g_WadList = g_slist_append( g_WadList, strdup( cleanwadname ) );
90 Sys_Printf( "wad: %s\n",cleanwadname );
97 g_WadList = g_slist_append( g_WadList, strdup( p1 ) );
98 Sys_Printf( "wad: %s\n",p1 );
102 p1 = p2 + 1; // point back to the remainder of the string
105 p1 = NULL; // make it so we exit the loop.
110 // strip the ".wad" extensions.
111 for ( GSList *l = g_WadList; l != NULL ; l = l->next )
113 p1 = (char *)l->data;
115 if ( p1[strlen( p1 ) - 4] == '.' ) {
116 p1[strlen( p1 ) - 4] = 0;
121 // FIXME: usefulness of this cache sounds very discutable
122 char *CheckCacheForTextureName( const char *cleantexturename ){
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 )
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 ;
140 char *AddToCache( const char *cleantexturename, const char *actualname ){
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 );
148 char *SearchWadsForTextureName( const char *cleantexturename ){
151 char *actualtexturename = NULL;
155 actualtexturename = CheckCacheForTextureName( cleantexturename );
156 if ( actualtexturename ) {
157 return actualtexturename;
160 // still here ? guess it's not in the cache then!
162 // search the wads listed in the worldspawn "wad" key
163 for ( l = g_WadList; l != NULL && actualtexturename == NULL ; l = l->next )
165 wadname = (char *)l->data;
167 str = new char[strlen( wadname ) + strlen( cleantexturename ) + 9 + 1 + 4 + 1];
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
174 sprintf( str,"textures/%s/%s.mip",wadname,cleantexturename );
175 count = vfsGetFileCount( str, VFS_SEARCH_PAK ); // only search pack files
179 // strip the extension, build the cache string and add the the cache
180 str[strlen( str ) - 4] = 0;
182 actualtexturename = AddToCache( cleantexturename,str );
184 //point the return value to the actual name, not what we add to the cache
185 actualtexturename += 1 + strlen( cleantexturename );
189 return actualtexturename;
191 // End of half-life specific stuff
193 void Patch_Parse( patchMesh_t *pPatch ){
197 char *token = Token();
199 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();
210 GetToken( true ); //(
212 // parse matrix dimensions
214 pPatch->width = atoi( token );
216 pPatch->height = atoi( token );
218 // ignore contents/flags/value
223 GetToken( false ); //)
226 GetToken( true ); //(
227 for ( i = 0; i < pPatch->width; i++ )
229 GetToken( true ); //(
230 for ( j = 0; j < pPatch->height; j++ )
232 GetToken( false ); //(
235 pPatch->ctrl[i][j].xyz[0] = atof( token );
237 pPatch->ctrl[i][j].xyz[1] = atof( token );
239 pPatch->ctrl[i][j].xyz[2] = atof( token );
241 pPatch->ctrl[i][j].st[0] = atof( token );
243 pPatch->ctrl[i][j].st[1] = atof( token );
245 GetToken( false ); //)
247 GetToken( false ); //)
249 GetToken( true ); //)
251 GetToken( true ); //}
254 void Face_Parse( face_t *face, bool bAlternateTexdef = false ){
257 bool bworldcraft = false;
259 char *token = Token();
263 for ( i = 0; i < 3; i++ )
265 GetToken( true ); //(
266 for ( j = 0; j < 3; j++ )
269 face->planepts[i][j] = atof( token );
271 GetToken( false ); //)
274 if ( bAlternateTexdef ) {
275 // parse alternate texdef
276 GetToken( false ); // (
277 GetToken( false ); // (
278 for ( i = 0; i < 3; i++ )
281 face->brushprimit_texdef.coords[0][i] = atof( token );
283 GetToken( false ); // )
284 GetToken( false ); // (
285 for ( i = 0; i < 3; i++ )
288 face->brushprimit_texdef.coords[1][i] = atof( token );
290 GetToken( false ); // )
291 GetToken( false ); // )
296 GetToken( false ); // shader
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.
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...)
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.
319 if ( g_MapVersion == MAPVERSION_HL ) {
320 qboolean done = false;
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
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
332 wadname = SearchWadsForTextureName( token );
335 face->texdef.SetName( wadname );
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 );
343 // end of half-life specific bit.
347 str = CheckCacheForTextureName( token );
349 face->texdef.SetName( str );
357 str = new char[strlen( token ) + 4 + 1];
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 );
364 // MIP support for quake
366 sprintf( str,"%s.mip",token );
367 fullpath = vfsGetFullPath( str, 0, 0 );
370 // TGA support in halflife ?
374 sprintf(str,"%s.tga",token);
375 fullpath = vfsGetFullPath(str);
381 // strip the extension.
382 int len = strlen( fullpath );
383 if ( fullpath[len - 4] == '.' ) {
384 fullpath[len - 4] = '\0';
387 // and set the correct name!
388 face->texdef.SetName( fullpath );
389 AddToCache( token,fullpath );
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 );
403 else // !MAPVERSION_HL
405 str = new char[strlen( token ) + 10];
406 strcpy( str, "textures/" );
407 strcpy( str + 9, token );
408 face->texdef.SetName( str );
412 if ( !bAlternateTexdef ) {
413 if ( g_MapVersion == MAPVERSION_HL ) { // Q1 as well ?
415 if ( token[0] == '[' && token[1] == '\0' ) {
418 GetToken( false ); // UAxis[0]
419 GetToken( false ); // UAxis[1]
420 GetToken( false ); // UAxis[2]
422 GetToken( false ); // shift
423 face->texdef.shift[0] = atof( token );
425 GetToken( false ); // ]
427 GetToken( false ); // [
428 GetToken( false ); // VAxis[0]
429 GetToken( false ); // VAxis[1]
430 GetToken( false ); // VAxis[2]
432 GetToken( false ); // shift
433 face->texdef.shift[1] = atof( token );
435 GetToken( false ); // ]
437 // rotation is derived from the U and V axes.
438 // ZHLT ignores this setting even if present in a .map file.
440 face->texdef.rotate = atof( token );
444 face->texdef.scale[0] = atof( token );
446 face->texdef.scale[1] = atof( token );
454 if ( !bworldcraft ) { // !MAPVERSION_HL
457 face->texdef.shift[0] = atof( token );
459 face->texdef.shift[1] = atof( token );
461 face->texdef.rotate = atof( token );
463 face->texdef.scale[0] = atof( token );
465 face->texdef.scale[1] = atof( token );
468 // parse the optional contents/flags/value
469 if ( !bworldcraft && TokenAvailable() ) {
471 if ( isdigit( token[0] ) ) {
472 face->texdef.contents = atoi( token );
474 face->texdef.flags = atoi( token );
476 face->texdef.value = atoi( token );
485 bool Primitive_Parse( brush_t *pBrush ){
486 char *token = Token();
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 ); //}
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;
506 else if ( !strcmp( token, "brushDef" ) ) {
507 pBrush->bBrushDef = true;
508 GetToken( true ); // {
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
517 if ( strcmp( token,"}" ) == 0 ) {
522 GetToken( true ); // }
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;
534 // check for end of brush
536 if ( strcmp( token,"}" ) == 0 ) {
545 void Entity_Parse( entity_t *pEntity ){
547 // CPtrArray *brushes = NULL;
548 char temptoken[1024];
550 char *token = Token();
554 GetToken( true ); // { or } or epair
555 if ( !strcmp( token, "}" ) ) {
558 else if ( !strcmp( token, "{" ) ) {
560 pBrush = Brush_Alloc();
561 if ( Primitive_Parse( pBrush ) ) {
562 ( (CPtrArray*)pEntity->pData )->Add( pBrush );
565 Brush_Free( pBrush, true );
571 strcpy( temptoken, token );
574 SetKeyValue( pEntity, temptoken, token );
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 );
587 void Map_Read( IDataStream *in, CPtrArray *map ){
591 unsigned long len = in->GetLength();
592 buf = new char[len + 1];
593 in->Read( buf, len );
595 StartTokenParsing( buf );
596 abortcode = MAP_NOERROR;
598 while ( abortcode == MAP_NOERROR )
600 if ( !GetToken( true ) ) { // { or NULL
603 pEntity = Entity_Alloc();
604 pEntity->pData = new CPtrArray;
605 Entity_Parse( pEntity );
611 if ( abortcode != MAP_NOERROR ) {
612 int num_ents, num_brushes,i,j;
616 num_ents = map->GetSize();
617 for ( i = 0; i < num_ents; i++ )
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++ )
624 Brush_Free( (brush_t *)brushes->GetAt( j ), true );
626 brushes->RemoveAll();
634 void Map_ReadQ3( IDataStream *in, CPtrArray *map ){
635 g_MapVersion = MAPVERSION_Q3;
639 void Map_ReadHL( IDataStream *in, CPtrArray *map ){
641 g_TextureNameCache = NULL;
643 g_MapVersion = MAPVERSION_HL;
646 FreeGSList( g_TextureNameCache );
647 FreeGSList( g_WadList );
650 void Map_ReadQ2( IDataStream *in, CPtrArray *map ){
651 g_MapVersion = MAPVERSION_Q2;