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