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