]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/bspfile.c
Merge branch 'illwieckz/gtkglext' into 'master'
[xonotic/netradiant.git] / tools / quake3 / common / bspfile.c
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
23 #include "cmdlib.h"
24 #include "mathlib.h"
25 #include "inout.h"
26 #include "bspfile.h"
27 #include "scriplib.h"
28
29 void GetLeafNums( void );
30
31 //=============================================================================
32
33 int bsp_version = Q3_BSP_VERSION;
34
35 int nummodels;
36 dmodel_t dmodels[MAX_MAP_MODELS];
37
38 int numShaders;
39 dshader_t dshaders[MAX_MAP_SHADERS];
40
41 int entdatasize;
42 char dentdata[MAX_MAP_ENTSTRING];
43
44 int numleafs;
45 dleaf_t dleafs[MAX_MAP_LEAFS];
46
47 int numplanes;
48 dplane_t dplanes[MAX_MAP_PLANES];
49
50 int numnodes;
51 dnode_t dnodes[MAX_MAP_NODES];
52
53 int numleafsurfaces;
54 int dleafsurfaces[MAX_MAP_LEAFFACES];
55
56 int numleafbrushes;
57 int dleafbrushes[MAX_MAP_LEAFBRUSHES];
58
59 int numbrushes;
60 dbrush_t dbrushes[MAX_MAP_BRUSHES];
61
62 int numbrushsides;
63 dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
64
65 int numLightBytes;
66 byte        *lightBytes;
67
68 int numGridPoints;
69 byte        *gridData;
70
71 int numVisBytes;
72 byte visBytes[MAX_MAP_VISIBILITY];
73
74 int numDrawVerts = 0;
75 int numDrawVertsBuffer = 0;
76 drawVert_t  *drawVerts = NULL;
77
78 int numDrawIndexes;
79 int drawIndexes[MAX_MAP_DRAW_INDEXES];
80
81 int numDrawSurfaces;
82 int numDrawSurfacesBuffer = 0;
83 dsurface_t  *drawSurfaces = NULL;
84
85 int numFogs;
86 dfog_t dfogs[MAX_MAP_FOGS];
87
88 void SetLightBytes( int n ){
89         if ( lightBytes != 0 ) {
90                 free( lightBytes );
91         }
92
93         numLightBytes = n;
94
95         if ( n == 0 ) {
96                 return;
97         }
98
99         lightBytes = safe_malloc0_info( numLightBytes, "SetLightBytes" );
100 }
101
102 void SetGridPoints( int n ){
103         if ( gridData != 0 ) {
104                 free( gridData );
105         }
106
107         numGridPoints = n;
108
109         if ( n == 0 ) {
110                 return;
111         }
112
113         gridData = safe_malloc0_info( numGridPoints * 8, "SetGridPoints" );
114 }
115
116 void IncDrawVerts(){
117         numDrawVerts++;
118
119         if ( drawVerts == 0 ) {
120                 numDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
121
122                 drawVerts = safe_malloc_info( sizeof( drawVert_t ) * numDrawVertsBuffer, "IncDrawVerts" );
123
124         }
125         else if ( numDrawVerts > numDrawVertsBuffer ) {
126                 numDrawVertsBuffer *= 3; // multiply by 1.5
127                 numDrawVertsBuffer /= 2;
128
129                 if ( numDrawVertsBuffer > MAX_MAP_DRAW_VERTS ) {
130                         numDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
131                 }
132
133                 drawVerts = realloc( drawVerts, sizeof( drawVert_t ) * numDrawVertsBuffer );
134
135                 if ( !drawVerts ) {
136                         Error( "realloc() failed (IncDrawVerts)" );
137                 }
138         }
139
140         memset( drawVerts + ( numDrawVerts - 1 ), 0, sizeof( drawVert_t ) );
141 }
142
143 void SetDrawVerts( int n ){
144         if ( drawVerts != 0 ) {
145                 free( drawVerts );
146         }
147
148         numDrawVerts = n;
149         numDrawVertsBuffer = numDrawVerts;
150
151         drawVerts = safe_malloc0_info( sizeof( drawVert_t ) * numDrawVertsBuffer, "IncDrawVerts" );
152 }
153
154 void SetDrawSurfacesBuffer(){
155         if ( drawSurfaces != 0 ) {
156                 free( drawSurfaces );
157         }
158
159         numDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
160
161         drawSurfaces = safe_malloc0_info( sizeof( dsurface_t ) * numDrawSurfacesBuffer, "IncDrawSurfaces" );
162 }
163
164 void SetDrawSurfaces( int n ){
165         if ( drawSurfaces != 0 ) {
166                 free( drawSurfaces );
167         }
168
169         numDrawSurfaces = n;
170         numDrawSurfacesBuffer = numDrawSurfaces;
171
172         drawSurfaces = safe_malloc0_info( sizeof( dsurface_t ) * numDrawSurfacesBuffer, "IncDrawSurfaces" );
173 }
174
175 void BspFilesCleanup(){
176         if ( drawVerts != 0 ) {
177                 free( drawVerts );
178         }
179         if ( drawSurfaces != 0 ) {
180                 free( drawSurfaces );
181         }
182         if ( lightBytes != 0 ) {
183                 free( lightBytes );
184         }
185         if ( gridData != 0 ) {
186                 free( gridData );
187         }
188 }
189
190 //=============================================================================
191
192 /*
193    =============
194    SwapBlock
195
196    If all values are 32 bits, this can be used to swap everything
197    =============
198  */
199 void SwapBlock( int *block, int sizeOfBlock ) {
200         int i;
201
202         sizeOfBlock >>= 2;
203         for ( i = 0 ; i < sizeOfBlock ; i++ ) {
204                 block[i] = LittleLong( block[i] );
205         }
206 }
207
208 /*
209    =============
210    SwapBSPFile
211
212    Byte swaps all data in a bsp file.
213    =============
214  */
215 void SwapBSPFile( void ) {
216         int i;
217
218         // models
219         SwapBlock( (int *)dmodels, nummodels * sizeof( dmodels[0] ) );
220
221         // shaders (don't swap the name)
222         for ( i = 0 ; i < numShaders ; i++ ) {
223                 dshaders[i].contentFlags = LittleLong( dshaders[i].contentFlags );
224                 dshaders[i].surfaceFlags = LittleLong( dshaders[i].surfaceFlags );
225         }
226
227         // planes
228         SwapBlock( (int *)dplanes, numplanes * sizeof( dplanes[0] ) );
229
230         // nodes
231         SwapBlock( (int *)dnodes, numnodes * sizeof( dnodes[0] ) );
232
233         // leafs
234         SwapBlock( (int *)dleafs, numleafs * sizeof( dleafs[0] ) );
235
236         // leaffaces
237         SwapBlock( (int *)dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ) );
238
239         // leafbrushes
240         SwapBlock( (int *)dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) );
241
242         // brushes
243         SwapBlock( (int *)dbrushes, numbrushes * sizeof( dbrushes[0] ) );
244
245         // brushsides
246         SwapBlock( (int *)dbrushsides, numbrushsides * sizeof( dbrushsides[0] ) );
247
248         // vis
249         ( (int *)&visBytes )[0] = LittleLong( ( (int *)&visBytes )[0] );
250         ( (int *)&visBytes )[1] = LittleLong( ( (int *)&visBytes )[1] );
251
252         // drawverts (don't swap colors )
253         for ( i = 0 ; i < numDrawVerts ; i++ ) {
254                 drawVerts[i].lightmap[0] = LittleFloat( drawVerts[i].lightmap[0] );
255                 drawVerts[i].lightmap[1] = LittleFloat( drawVerts[i].lightmap[1] );
256                 drawVerts[i].st[0] = LittleFloat( drawVerts[i].st[0] );
257                 drawVerts[i].st[1] = LittleFloat( drawVerts[i].st[1] );
258                 drawVerts[i].xyz[0] = LittleFloat( drawVerts[i].xyz[0] );
259                 drawVerts[i].xyz[1] = LittleFloat( drawVerts[i].xyz[1] );
260                 drawVerts[i].xyz[2] = LittleFloat( drawVerts[i].xyz[2] );
261                 drawVerts[i].normal[0] = LittleFloat( drawVerts[i].normal[0] );
262                 drawVerts[i].normal[1] = LittleFloat( drawVerts[i].normal[1] );
263                 drawVerts[i].normal[2] = LittleFloat( drawVerts[i].normal[2] );
264         }
265
266         // drawindexes
267         SwapBlock( (int *)drawIndexes, numDrawIndexes * sizeof( drawIndexes[0] ) );
268
269         // drawsurfs
270         SwapBlock( (int *)drawSurfaces, numDrawSurfaces * sizeof( drawSurfaces[0] ) );
271
272         // fogs
273         for ( i = 0 ; i < numFogs ; i++ ) {
274                 dfogs[i].brushNum = LittleLong( dfogs[i].brushNum );
275                 dfogs[i].visibleSide = LittleLong( dfogs[i].visibleSide );
276         }
277 }
278
279
280
281 /*
282    =============
283    GetLumpElements
284    =============
285  */
286 int GetLumpElements( dheader_t  *header, int lump, int size ) {
287         int length = header->lumps[lump].filelen;
288
289         if ( length % size ) {
290                 Error( "LoadBSPFile: odd lump size" );
291         }
292
293         return length / size;
294 }
295
296 /*
297    =============
298    CopyLump
299    =============
300  */
301 int CopyLump( dheader_t *header, int lump, void *dest, int size ) {
302         int length, ofs;
303
304         length = header->lumps[lump].filelen;
305         ofs = header->lumps[lump].fileofs;
306
307         if ( length == 0 ) {
308                 return 0;
309         }
310
311         if ( length % size ) {
312                 Error( "LoadBSPFile: odd lump size" );
313         }
314
315         memcpy( dest, (byte *)header + ofs, length );
316
317         return length / size;
318 }
319
320 /*
321    =============
322    LoadBSPFile
323    =============
324  */
325 void    LoadBSPFile( const char *filename ) {
326         dheader_t   *header;
327
328         // load the file header
329         LoadFile( filename, (void **)&header );
330
331         // swap the header
332         SwapBlock( (int *)header, sizeof( *header ) );
333
334         if ( header->ident != BSP_IDENT ) {
335                 Error( "%s is not a IBSP file", filename );
336         }
337         if ( header->version != bsp_version ) {
338                 Error( "%s is version %i, not %i", filename, header->version, bsp_version );
339         }
340
341         numShaders = CopyLump( header, LUMP_SHADERS, dshaders, sizeof( dshader_t ) );
342         nummodels = CopyLump( header, LUMP_MODELS, dmodels, sizeof( dmodel_t ) );
343         numplanes = CopyLump( header, LUMP_PLANES, dplanes, sizeof( dplane_t ) );
344         numleafs = CopyLump( header, LUMP_LEAFS, dleafs, sizeof( dleaf_t ) );
345         numnodes = CopyLump( header, LUMP_NODES, dnodes, sizeof( dnode_t ) );
346         numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, dleafsurfaces, sizeof( dleafsurfaces[0] ) );
347         numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, dleafbrushes, sizeof( dleafbrushes[0] ) );
348         numbrushes = CopyLump( header, LUMP_BRUSHES, dbrushes, sizeof( dbrush_t ) );
349         numbrushsides = CopyLump( header, LUMP_BRUSHSIDES, dbrushsides, sizeof( dbrushside_t ) );
350         numDrawVerts = GetLumpElements( header, LUMP_DRAWVERTS, sizeof( drawVert_t ) );
351         SetDrawVerts( numDrawVerts );
352         CopyLump( header, LUMP_DRAWVERTS, drawVerts, sizeof( drawVert_t ) );
353         numDrawSurfaces = GetLumpElements( header, LUMP_SURFACES, sizeof( dsurface_t ) );
354         SetDrawSurfaces( numDrawSurfaces );
355         numDrawSurfaces = CopyLump( header, LUMP_SURFACES, drawSurfaces, sizeof( dsurface_t ) );
356         numFogs = CopyLump( header, LUMP_FOGS, dfogs, sizeof( dfog_t ) );
357         numDrawIndexes = CopyLump( header, LUMP_DRAWINDEXES, drawIndexes, sizeof( drawIndexes[0] ) );
358
359         numVisBytes = CopyLump( header, LUMP_VISIBILITY, visBytes, 1 );
360         numLightBytes = GetLumpElements( header, LUMP_LIGHTMAPS, 1 );
361         SetLightBytes( numLightBytes );
362         CopyLump( header, LUMP_LIGHTMAPS, lightBytes, 1 );
363         entdatasize = CopyLump( header, LUMP_ENTITIES, dentdata, 1 );
364
365         numGridPoints = GetLumpElements( header, LUMP_LIGHTGRID, 8 );
366         SetGridPoints( numGridPoints );
367         CopyLump( header, LUMP_LIGHTGRID, gridData, 8 );
368
369
370         free( header );     // everything has been copied out
371
372         // swap everything
373         SwapBSPFile();
374 }
375
376
377 //============================================================================
378
379 /*
380    =============
381    AddLump
382    =============
383  */
384 void AddLump( FILE *bspfile, dheader_t *header, int lumpnum, const void *data, int len ) {
385         lump_t *lump;
386
387         lump = &header->lumps[lumpnum];
388
389         lump->fileofs = LittleLong( ftell( bspfile ) );
390         lump->filelen = LittleLong( len );
391         SafeWrite( bspfile, data, ( len + 3 ) & ~3 );
392 }
393
394 /*
395    =============
396    WriteBSPFile
397
398    Swaps the bsp file in place, so it should not be referenced again
399    =============
400  */
401 void    WriteBSPFile( const char *filename ) {
402         dheader_t outheader, *header;
403         FILE        *bspfile;
404
405         header = &outheader;
406         memset( header, 0, sizeof( dheader_t ) );
407
408         SwapBSPFile();
409
410         header->ident = LittleLong( BSP_IDENT );
411         header->version = LittleLong( bsp_version );
412
413         bspfile = SafeOpenWrite( filename );
414         SafeWrite( bspfile, header, sizeof( dheader_t ) );    // overwritten later
415
416         AddLump( bspfile, header, LUMP_SHADERS, dshaders, numShaders * sizeof( dshader_t ) );
417         AddLump( bspfile, header, LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ) );
418         AddLump( bspfile, header, LUMP_LEAFS, dleafs, numleafs * sizeof( dleaf_t ) );
419         AddLump( bspfile, header, LUMP_NODES, dnodes, numnodes * sizeof( dnode_t ) );
420         AddLump( bspfile, header, LUMP_BRUSHES, dbrushes, numbrushes * sizeof( dbrush_t ) );
421         AddLump( bspfile, header, LUMP_BRUSHSIDES, dbrushsides, numbrushsides * sizeof( dbrushside_t ) );
422         AddLump( bspfile, header, LUMP_LEAFSURFACES, dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ) );
423         AddLump( bspfile, header, LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) );
424         AddLump( bspfile, header, LUMP_MODELS, dmodels, nummodels * sizeof( dmodel_t ) );
425         AddLump( bspfile, header, LUMP_DRAWVERTS, drawVerts, numDrawVerts * sizeof( drawVert_t ) );
426         AddLump( bspfile, header, LUMP_SURFACES, drawSurfaces, numDrawSurfaces * sizeof( dsurface_t ) );
427         AddLump( bspfile, header, LUMP_VISIBILITY, visBytes, numVisBytes );
428         AddLump( bspfile, header, LUMP_LIGHTMAPS, lightBytes, numLightBytes );
429         AddLump( bspfile, header, LUMP_LIGHTGRID, gridData, 8 * numGridPoints );
430         AddLump( bspfile, header, LUMP_ENTITIES, dentdata, entdatasize );
431         AddLump( bspfile, header, LUMP_FOGS, dfogs, numFogs * sizeof( dfog_t ) );
432         AddLump( bspfile, header, LUMP_DRAWINDEXES, drawIndexes, numDrawIndexes * sizeof( drawIndexes[0] ) );
433
434         fseek( bspfile, 0, SEEK_SET );
435         SafeWrite( bspfile, header, sizeof( dheader_t ) );
436         fclose( bspfile );
437 }
438
439 //============================================================================
440
441 /*
442    =============
443    PrintBSPFileSizes
444
445    Dumps info about current file
446    =============
447  */
448 void PrintBSPFileSizes( void ) {
449         if ( !num_entities ) {
450                 ParseEntities();
451         }
452
453         Sys_Printf( "%6i models       %7i\n"
454                                 ,nummodels, (int)( nummodels * sizeof( dmodel_t ) ) );
455         Sys_Printf( "%6i shaders      %7i\n"
456                                 ,numShaders, (int)( numShaders * sizeof( dshader_t ) ) );
457         Sys_Printf( "%6i brushes      %7i\n"
458                                 ,numbrushes, (int)( numbrushes * sizeof( dbrush_t ) ) );
459         Sys_Printf( "%6i brushsides   %7i\n"
460                                 ,numbrushsides, (int)( numbrushsides * sizeof( dbrushside_t ) ) );
461         Sys_Printf( "%6i fogs         %7i\n"
462                                 ,numFogs, (int)( numFogs * sizeof( dfog_t ) ) );
463         Sys_Printf( "%6i planes       %7i\n"
464                                 ,numplanes, (int)( numplanes * sizeof( dplane_t ) ) );
465         Sys_Printf( "%6i entdata      %7i\n", num_entities, entdatasize );
466
467         Sys_Printf( "\n" );
468
469         Sys_Printf( "%6i nodes        %7i\n"
470                                 ,numnodes, (int)( numnodes * sizeof( dnode_t ) ) );
471         Sys_Printf( "%6i leafs        %7i\n"
472                                 ,numleafs, (int)( numleafs * sizeof( dleaf_t ) ) );
473         Sys_Printf( "%6i leafsurfaces %7i\n"
474                                 ,numleafsurfaces, (int)( numleafsurfaces * sizeof( dleafsurfaces[0] ) ) );
475         Sys_Printf( "%6i leafbrushes  %7i\n"
476                                 ,numleafbrushes, (int)( numleafbrushes * sizeof( dleafbrushes[0] ) ) );
477         Sys_Printf( "%6i drawverts    %7i\n"
478                                 ,numDrawVerts, (int)( numDrawVerts * sizeof( drawVerts[0] ) ) );
479         Sys_Printf( "%6i drawindexes  %7i\n"
480                                 ,numDrawIndexes, (int)( numDrawIndexes * sizeof( drawIndexes[0] ) ) );
481         Sys_Printf( "%6i drawsurfaces %7i\n"
482                                 ,numDrawSurfaces, (int)( numDrawSurfaces * sizeof( drawSurfaces[0] ) ) );
483
484         Sys_Printf( "%6i lightmaps    %7i\n"
485                                 ,numLightBytes / ( LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3 ), numLightBytes );
486         Sys_Printf( "       visibility   %7i\n"
487                                 , numVisBytes );
488 }
489
490
491 //============================================
492
493 int num_entities;
494 entity_t entities[MAX_MAP_ENTITIES];
495
496 void StripTrailing( char *e ) {
497         char    *s;
498
499         s = e + strlen( e ) - 1;
500         while ( s >= e && *s <= 32 )
501         {
502                 *s = 0;
503                 s--;
504         }
505 }
506
507 /*
508    =================
509    ParseEpair
510    =================
511  */
512 epair_t *ParseEpair( void ) {
513         epair_t *e;
514
515         e = safe_malloc0( sizeof( epair_t ) );
516
517         if ( strlen( token ) >= MAX_KEY - 1 ) {
518                 Error( "ParseEpar: token too long" );
519         }
520         e->key = copystring( token );
521         GetToken( qfalse );
522         if ( strlen( token ) >= MAX_VALUE - 1 ) {
523                 Error( "ParseEpar: token too long" );
524         }
525         e->value = copystring( token );
526
527         // strip trailing spaces that sometimes get accidentally
528         // added in the editor
529         StripTrailing( e->key );
530         StripTrailing( e->value );
531
532         return e;
533 }
534
535
536 /*
537    ================
538    ParseEntity
539    ================
540  */
541 qboolean    ParseEntity( void ) {
542         epair_t     *e;
543         entity_t    *mapent;
544
545         if ( !GetToken( qtrue ) ) {
546                 return qfalse;
547         }
548
549         if ( strcmp( token, "{" ) ) {
550                 Error( "ParseEntity: { not found" );
551         }
552         if ( num_entities == MAX_MAP_ENTITIES ) {
553                 Error( "num_entities == MAX_MAP_ENTITIES" );
554         }
555         mapent = &entities[num_entities];
556         num_entities++;
557
558         do {
559                 if ( !GetToken( qtrue ) ) {
560                         Error( "ParseEntity: EOF without closing brace" );
561                 }
562                 if ( !strcmp( token, "}" ) ) {
563                         break;
564                 }
565                 e = ParseEpair();
566                 e->next = mapent->epairs;
567                 mapent->epairs = e;
568         } while ( 1 );
569
570         return qtrue;
571 }
572
573 /*
574    ================
575    ParseEntities
576
577    Parses the dentdata string into entities
578    ================
579  */
580 void ParseEntities( void ) {
581         num_entities = 0;
582         ParseFromMemory( dentdata, entdatasize );
583
584         while ( ParseEntity() ) {
585         }
586 }
587
588
589 /*
590    ================
591    UnparseEntities
592
593    Generates the dentdata string from all the entities
594    This allows the utilities to add or remove key/value pairs
595    to the data created by the map editor.
596    ================
597  */
598 void UnparseEntities( void ) {
599         char    *buf, *end;
600         epair_t *ep;
601         char line[2048];
602         int i;
603         char key[1024], value[1024];
604
605         buf = dentdata;
606         end = buf;
607         *end = 0;
608
609         for ( i = 0 ; i < num_entities ; i++ ) {
610                 ep = entities[i].epairs;
611                 if ( !ep ) {
612                         continue;   // ent got removed
613                 }
614
615                 strcat( end,"{\n" );
616                 end += 2;
617
618                 for ( ep = entities[i].epairs ; ep ; ep = ep->next ) {
619                         strcpy( key, ep->key );
620                         StripTrailing( key );
621                         strcpy( value, ep->value );
622                         StripTrailing( value );
623
624                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
625                         strcat( end, line );
626                         end += strlen( line );
627                 }
628                 strcat( end,"}\n" );
629                 end += 2;
630
631                 if ( end > buf + MAX_MAP_ENTSTRING ) {
632                         Error( "Entity text too long" );
633                 }
634         }
635         entdatasize = end - buf + 1;
636 }
637
638 void PrintEntity( const entity_t *ent ) {
639         epair_t *ep;
640
641         Sys_Printf( "------- entity %p -------\n", ent );
642         for ( ep = ent->epairs ; ep ; ep = ep->next ) {
643                 Sys_Printf( "%s = %s\n", ep->key, ep->value );
644         }
645
646 }
647
648 void    SetKeyValue( entity_t *ent, const char *key, const char *value ) {
649         epair_t *ep;
650
651         for ( ep = ent->epairs ; ep ; ep = ep->next ) {
652                 if ( !strcmp( ep->key, key ) ) {
653                         free( ep->value );
654                         ep->value = copystring( value );
655                         return;
656                 }
657         }
658         ep = safe_malloc( sizeof( *ep ) );
659         ep->next = ent->epairs;
660         ent->epairs = ep;
661         ep->key = copystring( key );
662         ep->value = copystring( value );
663 }
664
665 const char  *ValueForKey( const entity_t *ent, const char *key ) {
666         epair_t *ep;
667
668         for ( ep = ent->epairs ; ep ; ep = ep->next ) {
669                 if ( !strcmp( ep->key, key ) ) {
670                         return ep->value;
671                 }
672         }
673         return "";
674 }
675
676 vec_t   FloatForKey( const entity_t *ent, const char *key ) {
677         const char  *k;
678
679         k = ValueForKey( ent, key );
680         return atof( k );
681 }
682
683 void    GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ) {
684         const char  *k;
685         double v1, v2, v3;
686
687         k = ValueForKey( ent, key );
688
689         // scanf into doubles, then assign, so it is vec_t size independent
690         v1 = v2 = v3 = 0;
691         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
692         vec[0] = v1;
693         vec[1] = v2;
694         vec[2] = v3;
695 }