]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/common/bspfile.c
Merge branch 'fix-fast' into 'master'
[xonotic/netradiant.git] / tools / quake2 / 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 #include "cmdlib.h"
23 #include "mathlib.h"
24 #include "bspfile.h"
25 #include "scriplib.h"
26 #include "inout.h"
27
28 void GetLeafNums( void );
29
30 //=============================================================================
31
32 int nummodels;
33 dmodel_t dmodels[MAX_MAP_MODELS];
34
35 int visdatasize;
36 byte dvisdata[MAX_MAP_VISIBILITY];
37 dvis_t      *dvis = (dvis_t *)dvisdata;
38
39 int lightdatasize;
40 byte dlightdata[MAX_MAP_LIGHTING];
41
42 int entdatasize;
43 char dentdata[MAX_MAP_ENTSTRING];
44
45 int numleafs;
46 dleaf_t dleafs[MAX_MAP_LEAFS];
47
48 int numplanes;
49 dplane_t dplanes[MAX_MAP_PLANES];
50
51 int numvertexes;
52 dvertex_t dvertexes[MAX_MAP_VERTS];
53
54 int numnodes;
55 dnode_t dnodes[MAX_MAP_NODES];
56
57 int numtexinfo;
58 texinfo_t texinfo[MAX_MAP_TEXINFO];
59
60 int numfaces;
61 dface_t dfaces[MAX_MAP_FACES];
62
63 int numedges;
64 dedge_t dedges[MAX_MAP_EDGES];
65
66 int numleaffaces;
67 unsigned short dleaffaces[MAX_MAP_LEAFFACES];
68
69 int numleafbrushes;
70 unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
71
72 int numsurfedges;
73 int dsurfedges[MAX_MAP_SURFEDGES];
74
75 int numbrushes;
76 dbrush_t dbrushes[MAX_MAP_BRUSHES];
77
78 int numbrushsides;
79 dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
80
81 int numareas;
82 darea_t dareas[MAX_MAP_AREAS];
83
84 int numareaportals;
85 dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
86
87 byte dpop[256];
88
89 /*
90    ===============
91    CompressVis
92
93    ===============
94  */
95 int CompressVis( byte *vis, byte *dest ){
96         int j;
97         int rep;
98         int visrow;
99         byte    *dest_p;
100
101         dest_p = dest;
102 //      visrow = (r_numvisleafs + 7)>>3;
103         visrow = ( dvis->numclusters + 7 ) >> 3;
104
105         for ( j = 0 ; j < visrow ; j++ )
106         {
107                 *dest_p++ = vis[j];
108                 if ( vis[j] ) {
109                         continue;
110                 }
111
112                 rep = 1;
113                 for ( j++; j < visrow ; j++ )
114                         if ( vis[j] || rep == 255 ) {
115                                 break;
116                         }
117                         else{
118                                 rep++;
119                         }
120                 *dest_p++ = rep;
121                 j--;
122         }
123
124         return dest_p - dest;
125 }
126
127
128 /*
129    ===================
130    DecompressVis
131    ===================
132  */
133 void DecompressVis( byte *in, byte *decompressed ){
134         int c;
135         byte    *out;
136         int row;
137
138 //      row = (r_numvisleafs+7)>>3;
139         row = ( dvis->numclusters + 7 ) >> 3;
140         out = decompressed;
141
142         do
143         {
144                 if ( *in ) {
145                         *out++ = *in++;
146                         continue;
147                 }
148
149                 c = in[1];
150                 if ( !c ) {
151                         Error( "DecompressVis: 0 repeat" );
152                 }
153                 in += 2;
154                 while ( c )
155                 {
156                         *out++ = 0;
157                         c--;
158                 }
159         } while ( out - decompressed < row );
160 }
161
162 //=============================================================================
163
164 /*
165    =============
166    SwapBSPFile
167
168    Byte swaps all data in a bsp file.
169    =============
170  */
171 void SwapBSPFile( qboolean todisk ){
172         int i, j;
173         dmodel_t        *d;
174
175
176 // models
177         for ( i = 0 ; i < nummodels ; i++ )
178         {
179                 d = &dmodels[i];
180
181                 d->firstface = LittleLong( d->firstface );
182                 d->numfaces = LittleLong( d->numfaces );
183                 d->headnode = LittleLong( d->headnode );
184
185                 for ( j = 0 ; j < 3 ; j++ )
186                 {
187                         d->mins[j] = LittleFloat( d->mins[j] );
188                         d->maxs[j] = LittleFloat( d->maxs[j] );
189                         d->origin[j] = LittleFloat( d->origin[j] );
190                 }
191         }
192
193 //
194 // vertexes
195 //
196         for ( i = 0 ; i < numvertexes ; i++ )
197         {
198                 for ( j = 0 ; j < 3 ; j++ )
199                         dvertexes[i].point[j] = LittleFloat( dvertexes[i].point[j] );
200         }
201
202 //
203 // planes
204 //
205         for ( i = 0 ; i < numplanes ; i++ )
206         {
207                 for ( j = 0 ; j < 3 ; j++ )
208                         dplanes[i].normal[j] = LittleFloat( dplanes[i].normal[j] );
209                 dplanes[i].dist = LittleFloat( dplanes[i].dist );
210                 dplanes[i].type = LittleLong( dplanes[i].type );
211         }
212
213 //
214 // texinfos
215 //
216         for ( i = 0 ; i < numtexinfo ; i++ )
217         {
218                 for ( j = 0 ; j < 8 ; j++ )
219                         texinfo[i].vecs[0][j] = LittleFloat( texinfo[i].vecs[0][j] );
220                 texinfo[i].flags = LittleLong( texinfo[i].flags );
221                 texinfo[i].value = LittleLong( texinfo[i].value );
222                 texinfo[i].nexttexinfo = LittleLong( texinfo[i].nexttexinfo );
223         }
224
225 //
226 // faces
227 //
228         for ( i = 0 ; i < numfaces ; i++ )
229         {
230                 dfaces[i].texinfo = LittleShort( dfaces[i].texinfo );
231                 dfaces[i].planenum = LittleShort( dfaces[i].planenum );
232                 dfaces[i].side = LittleShort( dfaces[i].side );
233                 dfaces[i].lightofs = LittleLong( dfaces[i].lightofs );
234                 dfaces[i].firstedge = LittleLong( dfaces[i].firstedge );
235                 dfaces[i].numedges = LittleShort( dfaces[i].numedges );
236         }
237
238 //
239 // nodes
240 //
241         for ( i = 0 ; i < numnodes ; i++ )
242         {
243                 dnodes[i].planenum = LittleLong( dnodes[i].planenum );
244                 for ( j = 0 ; j < 3 ; j++ )
245                 {
246                         dnodes[i].mins[j] = LittleShort( dnodes[i].mins[j] );
247                         dnodes[i].maxs[j] = LittleShort( dnodes[i].maxs[j] );
248                 }
249                 dnodes[i].children[0] = LittleLong( dnodes[i].children[0] );
250                 dnodes[i].children[1] = LittleLong( dnodes[i].children[1] );
251                 dnodes[i].firstface = LittleShort( dnodes[i].firstface );
252                 dnodes[i].numfaces = LittleShort( dnodes[i].numfaces );
253         }
254
255 //
256 // leafs
257 //
258         for ( i = 0 ; i < numleafs ; i++ )
259         {
260                 dleafs[i].contents = LittleLong( dleafs[i].contents );
261                 dleafs[i].cluster = LittleShort( dleafs[i].cluster );
262                 dleafs[i].area = LittleShort( dleafs[i].area );
263                 for ( j = 0 ; j < 3 ; j++ )
264                 {
265                         dleafs[i].mins[j] = LittleShort( dleafs[i].mins[j] );
266                         dleafs[i].maxs[j] = LittleShort( dleafs[i].maxs[j] );
267                 }
268
269                 dleafs[i].firstleafface = LittleShort( dleafs[i].firstleafface );
270                 dleafs[i].numleaffaces = LittleShort( dleafs[i].numleaffaces );
271                 dleafs[i].firstleafbrush = LittleShort( dleafs[i].firstleafbrush );
272                 dleafs[i].numleafbrushes = LittleShort( dleafs[i].numleafbrushes );
273         }
274
275 //
276 // leaffaces
277 //
278         for ( i = 0 ; i < numleaffaces ; i++ )
279                 dleaffaces[i] = LittleShort( dleaffaces[i] );
280
281 //
282 // leafbrushes
283 //
284         for ( i = 0 ; i < numleafbrushes ; i++ )
285                 dleafbrushes[i] = LittleShort( dleafbrushes[i] );
286
287 //
288 // surfedges
289 //
290         for ( i = 0 ; i < numsurfedges ; i++ )
291                 dsurfedges[i] = LittleLong( dsurfedges[i] );
292
293 //
294 // edges
295 //
296         for ( i = 0 ; i < numedges ; i++ )
297         {
298                 dedges[i].v[0] = LittleShort( dedges[i].v[0] );
299                 dedges[i].v[1] = LittleShort( dedges[i].v[1] );
300         }
301
302 //
303 // brushes
304 //
305         for ( i = 0 ; i < numbrushes ; i++ )
306         {
307                 dbrushes[i].firstside = LittleLong( dbrushes[i].firstside );
308                 dbrushes[i].numsides = LittleLong( dbrushes[i].numsides );
309                 dbrushes[i].contents = LittleLong( dbrushes[i].contents );
310         }
311
312 //
313 // areas
314 //
315         for ( i = 0 ; i < numareas ; i++ )
316         {
317                 dareas[i].numareaportals = LittleLong( dareas[i].numareaportals );
318                 dareas[i].firstareaportal = LittleLong( dareas[i].firstareaportal );
319         }
320
321 //
322 // areasportals
323 //
324         for ( i = 0 ; i < numareaportals ; i++ )
325         {
326                 dareaportals[i].portalnum = LittleLong( dareaportals[i].portalnum );
327                 dareaportals[i].otherarea = LittleLong( dareaportals[i].otherarea );
328         }
329
330 //
331 // brushsides
332 //
333         for ( i = 0 ; i < numbrushsides ; i++ )
334         {
335                 dbrushsides[i].planenum = LittleShort( dbrushsides[i].planenum );
336                 dbrushsides[i].texinfo = LittleShort( dbrushsides[i].texinfo );
337         }
338
339 //
340 // visibility
341 //
342         if ( todisk ) {
343                 j = dvis->numclusters;
344         }
345         else{
346                 j = LittleLong( dvis->numclusters );
347         }
348         dvis->numclusters = LittleLong( dvis->numclusters );
349         for ( i = 0 ; i < j ; i++ )
350         {
351                 dvis->bitofs[i][0] = LittleLong( dvis->bitofs[i][0] );
352                 dvis->bitofs[i][1] = LittleLong( dvis->bitofs[i][1] );
353         }
354 }
355
356
357 dheader_t   *header;
358
359 int CopyLump( int lump, void *dest, int size ){
360         int length, ofs;
361
362         length = header->lumps[lump].filelen;
363         ofs = header->lumps[lump].fileofs;
364
365         if ( length % size ) {
366                 Error( "LoadBSPFile: odd lump size" );
367         }
368
369         memcpy( dest, (byte *)header + ofs, length );
370
371         return length / size;
372 }
373
374 /*
375    =============
376    LoadBSPFile
377    =============
378  */
379 void    LoadBSPFile( char *filename ){
380         int i;
381
382 //
383 // load the file header
384 //
385         LoadFile( filename, (void **)&header );
386
387 // swap the header
388         for ( i = 0 ; i < sizeof( dheader_t ) / 4 ; i++ )
389                 ( (int *)header )[i] = LittleLong( ( (int *)header )[i] );
390
391         if ( header->ident != IDBSPHEADER ) {
392                 Error( "%s is not a IBSP file", filename );
393         }
394         if ( header->version != BSPVERSION ) {
395                 Error( "%s is version %i, not %i", filename, header->version, BSPVERSION );
396         }
397
398         nummodels = CopyLump( LUMP_MODELS, dmodels, sizeof( dmodel_t ) );
399         numvertexes = CopyLump( LUMP_VERTEXES, dvertexes, sizeof( dvertex_t ) );
400         numplanes = CopyLump( LUMP_PLANES, dplanes, sizeof( dplane_t ) );
401         numleafs = CopyLump( LUMP_LEAFS, dleafs, sizeof( dleaf_t ) );
402         numnodes = CopyLump( LUMP_NODES, dnodes, sizeof( dnode_t ) );
403         numtexinfo = CopyLump( LUMP_TEXINFO, texinfo, sizeof( texinfo_t ) );
404         numfaces = CopyLump( LUMP_FACES, dfaces, sizeof( dface_t ) );
405         numleaffaces = CopyLump( LUMP_LEAFFACES, dleaffaces, sizeof( dleaffaces[0] ) );
406         numleafbrushes = CopyLump( LUMP_LEAFBRUSHES, dleafbrushes, sizeof( dleafbrushes[0] ) );
407         numsurfedges = CopyLump( LUMP_SURFEDGES, dsurfedges, sizeof( dsurfedges[0] ) );
408         numedges = CopyLump( LUMP_EDGES, dedges, sizeof( dedge_t ) );
409         numbrushes = CopyLump( LUMP_BRUSHES, dbrushes, sizeof( dbrush_t ) );
410         numbrushsides = CopyLump( LUMP_BRUSHSIDES, dbrushsides, sizeof( dbrushside_t ) );
411         numareas = CopyLump( LUMP_AREAS, dareas, sizeof( darea_t ) );
412         numareaportals = CopyLump( LUMP_AREAPORTALS, dareaportals, sizeof( dareaportal_t ) );
413
414         visdatasize = CopyLump( LUMP_VISIBILITY, dvisdata, 1 );
415         lightdatasize = CopyLump( LUMP_LIGHTING, dlightdata, 1 );
416         entdatasize = CopyLump( LUMP_ENTITIES, dentdata, 1 );
417
418         CopyLump( LUMP_POP, dpop, 1 );
419
420         free( header );      // everything has been copied out
421
422 //
423 // swap everything
424 //
425         SwapBSPFile( false );
426 }
427
428
429 /*
430    =============
431    LoadBSPFileTexinfo
432
433    Only loads the texinfo lump, so qdata can scan for textures
434    =============
435  */
436 void    LoadBSPFileTexinfo( char *filename ){
437         int i;
438         FILE        *f;
439         int length, ofs;
440
441         header = malloc( sizeof( dheader_t ) );
442
443         f = fopen( filename, "rb" );
444         fread( header, sizeof( dheader_t ), 1, f );
445
446 // swap the header
447         for ( i = 0 ; i < sizeof( dheader_t ) / 4 ; i++ )
448                 ( (int *)header )[i] = LittleLong( ( (int *)header )[i] );
449
450         if ( header->ident != IDBSPHEADER ) {
451                 Error( "%s is not a IBSP file", filename );
452         }
453         if ( header->version != BSPVERSION ) {
454                 Error( "%s is version %i, not %i", filename, header->version, BSPVERSION );
455         }
456
457
458         length = header->lumps[LUMP_TEXINFO].filelen;
459         ofs = header->lumps[LUMP_TEXINFO].fileofs;
460
461         fseek( f, ofs, SEEK_SET );
462         fread( texinfo, length, 1, f );
463         fclose( f );
464
465         numtexinfo = length / sizeof( texinfo_t );
466
467         free( header );      // everything has been copied out
468
469         SwapBSPFile( false );
470 }
471
472
473 //============================================================================
474
475 FILE        *wadfile;
476 dheader_t outheader;
477
478 void AddLump( int lumpnum, void *data, int len ){
479         lump_t *lump;
480
481         lump = &header->lumps[lumpnum];
482
483         lump->fileofs = LittleLong( ftell( wadfile ) );
484         lump->filelen = LittleLong( len );
485         SafeWrite( wadfile, data, ( len + 3 ) & ~3 );
486 }
487
488 /*
489    =============
490    WriteBSPFile
491
492    Swaps the bsp file in place, so it should not be referenced again
493    =============
494  */
495 void    WriteBSPFile( char *filename ){
496         header = &outheader;
497         memset( header, 0, sizeof( dheader_t ) );
498
499         SwapBSPFile( true );
500
501         header->ident = LittleLong( IDBSPHEADER );
502         header->version = LittleLong( BSPVERSION );
503
504         wadfile = SafeOpenWrite( filename );
505         SafeWrite( wadfile, header, sizeof( dheader_t ) ); // overwritten later
506
507         AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ) );
508         AddLump( LUMP_LEAFS, dleafs, numleafs * sizeof( dleaf_t ) );
509         AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertex_t ) );
510         AddLump( LUMP_NODES, dnodes, numnodes * sizeof( dnode_t ) );
511         AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo_t ) );
512         AddLump( LUMP_FACES, dfaces, numfaces * sizeof( dface_t ) );
513         AddLump( LUMP_BRUSHES, dbrushes, numbrushes * sizeof( dbrush_t ) );
514         AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides * sizeof( dbrushside_t ) );
515         AddLump( LUMP_LEAFFACES, dleaffaces, numleaffaces * sizeof( dleaffaces[0] ) );
516         AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) );
517         AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[0] ) );
518         AddLump( LUMP_EDGES, dedges, numedges * sizeof( dedge_t ) );
519         AddLump( LUMP_MODELS, dmodels, nummodels * sizeof( dmodel_t ) );
520         AddLump( LUMP_AREAS, dareas, numareas * sizeof( darea_t ) );
521         AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals * sizeof( dareaportal_t ) );
522
523         AddLump( LUMP_LIGHTING, dlightdata, lightdatasize );
524         AddLump( LUMP_VISIBILITY, dvisdata, visdatasize );
525         AddLump( LUMP_ENTITIES, dentdata, entdatasize );
526         AddLump( LUMP_POP, dpop, sizeof( dpop ) );
527
528         fseek( wadfile, 0, SEEK_SET );
529         SafeWrite( wadfile, header, sizeof( dheader_t ) );
530         fclose( wadfile );
531 }
532
533 //============================================================================
534
535 /*
536    =============
537    PrintBSPFileSizes
538
539    Dumps info about current file
540    =============
541  */
542 void PrintBSPFileSizes( void ){
543         if ( !num_entities ) {
544                 ParseEntities();
545         }
546
547         printf( "%5i models       %7i\n"
548                         ,nummodels, (int)( nummodels * sizeof( dmodel_t ) ) );
549         printf( "%5i brushes      %7i\n"
550                         ,numbrushes, (int)( numbrushes * sizeof( dbrush_t ) ) );
551         printf( "%5i brushsides   %7i\n"
552                         ,numbrushsides, (int)( numbrushsides * sizeof( dbrushside_t ) ) );
553         printf( "%5i planes       %7i\n"
554                         ,numplanes, (int)( numplanes * sizeof( dplane_t ) ) );
555         printf( "%5i texinfo      %7i\n"
556                         ,numtexinfo, (int)( numtexinfo * sizeof( texinfo_t ) ) );
557         printf( "%5i entdata      %7i\n", num_entities, entdatasize );
558
559         printf( "\n" );
560
561         printf( "%5i vertexes     %7i\n"
562                         ,numvertexes, (int)( numvertexes * sizeof( dvertex_t ) ) );
563         printf( "%5i nodes        %7i\n"
564                         ,numnodes, (int)( numnodes * sizeof( dnode_t ) ) );
565         printf( "%5i faces        %7i\n"
566                         ,numfaces, (int)( numfaces * sizeof( dface_t ) ) );
567         printf( "%5i leafs        %7i\n"
568                         ,numleafs, (int)( numleafs * sizeof( dleaf_t ) ) );
569         printf( "%5i leaffaces    %7i\n"
570                         ,numleaffaces, (int)( numleaffaces * sizeof( dleaffaces[0] ) ) );
571         printf( "%5i leafbrushes  %7i\n"
572                         ,numleafbrushes, (int)( numleafbrushes * sizeof( dleafbrushes[0] ) ) );
573         printf( "%5i surfedges    %7i\n"
574                         ,numsurfedges, (int)( numsurfedges * sizeof( dsurfedges[0] ) ) );
575         printf( "%5i edges        %7i\n"
576                         ,numedges, (int)( numedges * sizeof( dedge_t ) ) );
577         printf( "      lightdata    %7i\n", lightdatasize );
578         printf( "      visdata      %7i\n", visdatasize );
579 }
580
581
582 //============================================
583
584 int num_entities;
585 entity_t entities[MAX_MAP_ENTITIES];
586
587 void StripTrailing( char *e ){
588         char    *s;
589
590         s = e + strlen( e ) - 1;
591         while ( s >= e && *s <= 32 )
592         {
593                 *s = 0;
594                 s--;
595         }
596 }
597
598 /*
599    =================
600    ParseEpair
601    =================
602  */
603 epair_t *ParseEpair( void ){
604         epair_t *e;
605
606         e = malloc( sizeof( epair_t ) );
607         memset( e, 0, sizeof( epair_t ) );
608
609         if ( strlen( token ) >= MAX_KEY - 1 ) {
610                 Error( "ParseEpar: token too long" );
611         }
612         e->key = copystring( token );
613         GetToken( false );
614         if ( strlen( token ) >= MAX_VALUE - 1 ) {
615                 Error( "ParseEpar: token too long" );
616         }
617         e->value = copystring( token );
618
619         // strip trailing spaces
620         StripTrailing( e->key );
621         StripTrailing( e->value );
622
623         return e;
624 }
625
626
627 /*
628    ================
629    ParseEntity
630    ================
631  */
632 qboolean    ParseEntity( void ){
633         epair_t     *e;
634         entity_t    *mapent;
635
636         if ( !GetToken( true ) ) {
637                 return false;
638         }
639
640         if ( strcmp( token, "{" ) ) {
641                 Error( "ParseEntity: { not found" );
642         }
643
644         if ( num_entities == MAX_MAP_ENTITIES ) {
645                 Error( "num_entities == MAX_MAP_ENTITIES" );
646         }
647
648         mapent = &entities[num_entities];
649         num_entities++;
650
651         do
652         {
653                 if ( !GetToken( true ) ) {
654                         Error( "ParseEntity: EOF without closing brace" );
655                 }
656                 if ( !strcmp( token, "}" ) ) {
657                         break;
658                 }
659                 e = ParseEpair();
660                 e->next = mapent->epairs;
661                 mapent->epairs = e;
662         } while ( 1 );
663
664         return true;
665 }
666
667 /*
668    ================
669    ParseEntities
670
671    Parses the dentdata string into entities
672    ================
673  */
674 void ParseEntities( void ){
675         num_entities = 0;
676         ParseFromMemory( dentdata, entdatasize );
677
678         while ( ParseEntity() )
679         {
680         }
681 }
682
683
684 /*
685    ================
686    UnparseEntities
687
688    Generates the dentdata string from all the entities
689    ================
690  */
691 void UnparseEntities( void ){
692         char    *buf, *end;
693         epair_t *ep;
694         char line[2048];
695         int i;
696         char key[1024], value[1024];
697
698         buf = dentdata;
699         end = buf;
700         *end = 0;
701
702         for ( i = 0 ; i < num_entities ; i++ )
703         {
704                 ep = entities[i].epairs;
705                 if ( !ep ) {
706                         continue;   // ent got removed
707
708                 }
709                 strcat( end,"{\n" );
710                 end += 2;
711
712                 for ( ep = entities[i].epairs ; ep ; ep = ep->next )
713                 {
714                         strcpy( key, ep->key );
715                         StripTrailing( key );
716                         strcpy( value, ep->value );
717                         StripTrailing( value );
718
719                         sprintf( line, "\"%s\" \"%s\"\n", key, value );
720                         strcat( end, line );
721                         end += strlen( line );
722                 }
723                 strcat( end,"}\n" );
724                 end += 2;
725
726                 if ( end > buf + MAX_MAP_ENTSTRING ) {
727                         Error( "Entity text too long" );
728                 }
729         }
730         entdatasize = end - buf + 1;
731 }
732
733 void PrintEntity( entity_t *ent ){
734         epair_t *ep;
735
736         printf( "------- entity %p -------\n", ent );
737         for ( ep = ent->epairs ; ep ; ep = ep->next )
738         {
739                 printf( "%s = %s\n", ep->key, ep->value );
740         }
741
742 }
743
744 void    SetKeyValue( entity_t *ent, char *key, char *value ){
745         epair_t *ep;
746
747         for ( ep = ent->epairs ; ep ; ep = ep->next )
748                 if ( !strcmp( ep->key, key ) ) {
749                         free( ep->value );
750                         ep->value = copystring( value );
751                         return;
752                 }
753         ep = malloc( sizeof( *ep ) );
754         ep->next = ent->epairs;
755         ent->epairs = ep;
756         ep->key = copystring( key );
757         ep->value = copystring( value );
758 }
759
760 char    *ValueForKey( entity_t *ent, char *key ){
761         epair_t *ep;
762
763         for ( ep = ent->epairs ; ep ; ep = ep->next )
764                 if ( !strcmp( ep->key, key ) ) {
765                         return ep->value;
766                 }
767         return "";
768 }
769
770 vec_t   FloatForKey( entity_t *ent, char *key ){
771         char    *k;
772
773         k = ValueForKey( ent, key );
774         return atof( k );
775 }
776
777 void    GetVectorForKey( entity_t *ent, char *key, vec3_t vec ){
778         char    *k;
779         double v1, v2, v3;
780
781         k = ValueForKey( ent, key );
782 // scanf into doubles, then assign, so it is vec_t size independent
783         v1 = v2 = v3 = 0;
784         sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
785         vec[0] = v1;
786         vec[1] = v2;
787         vec[2] = v3;
788 }