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