Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

map.c (35826B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, 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 Foobar; 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 "qbsp.h"
     24 #include "l_bsp_hl.h"
     25 #include "l_bsp_q1.h"
     26 #include "l_bsp_q2.h"
     27 #include "l_bsp_q3.h"
     28 #include "l_bsp_sin.h"
     29 #include "l_mem.h"
     30 #include "../botlib/aasfile.h"		//aas_bbox_t
     31 #include "aas_store.h"		//AAS_MAX_BBOXES
     32 #include "aas_cfg.h"
     33 
     34 #define Sign(x)		(x < 0 ? 1 : 0)
     35 
     36 int					nummapbrushes;
     37 mapbrush_t			mapbrushes[MAX_MAPFILE_BRUSHES];
     38 
     39 int					nummapbrushsides;
     40 side_t				brushsides[MAX_MAPFILE_BRUSHSIDES];
     41 brush_texture_t		side_brushtextures[MAX_MAPFILE_BRUSHSIDES];
     42 
     43 int					nummapplanes;
     44 plane_t				mapplanes[MAX_MAPFILE_PLANES];
     45 int					mapplaneusers[MAX_MAPFILE_PLANES];
     46 
     47 #define				PLANE_HASHES	1024
     48 plane_t				*planehash[PLANE_HASHES];
     49 vec3_t				map_mins, map_maxs;
     50 
     51 #ifdef SIN
     52 textureref_t		side_newrefs[MAX_MAPFILE_BRUSHSIDES];
     53 #endif
     54 
     55 map_texinfo_t		map_texinfo[MAX_MAPFILE_TEXINFO];
     56 int					map_numtexinfo;
     57 int					loadedmaptype;		//loaded map type
     58 
     59 // undefine to make plane finding use linear sort
     60 #define	USE_HASHING
     61 
     62 int c_boxbevels;
     63 int c_edgebevels;
     64 int c_areaportals;
     65 int c_clipbrushes;
     66 int c_squattbrushes;
     67 int c_writtenbrushes;
     68 
     69 /*
     70 =============================================================================
     71 
     72 PLANE FINDING
     73 
     74 =============================================================================
     75 */
     76 
     77 
     78 //===========================================================================
     79 //
     80 // Parameter:				-
     81 // Returns:					-
     82 // Changes Globals:		-
     83 //===========================================================================
     84 int PlaneSignBits(vec3_t normal)
     85 {
     86 	int i, signbits;
     87 
     88 	signbits = 0;
     89 	for (i = 2; i >= 0; i--)
     90 	{
     91 		signbits = (signbits << 1) + Sign(normal[i]);
     92 	} //end for
     93 	return signbits;
     94 } //end of the function PlaneSignBits
     95 //===========================================================================
     96 //
     97 // Parameter:				-
     98 // Returns:					-
     99 // Changes Globals:		-
    100 //===========================================================================
    101 int PlaneTypeForNormal(vec3_t normal)
    102 {
    103 	vec_t	ax, ay, az;
    104 	
    105 // NOTE: should these have an epsilon around 1.0?		
    106 	if (normal[0] == 1.0 || normal[0] == -1.0)
    107 		return PLANE_X;
    108 	if (normal[1] == 1.0 || normal[1] == -1.0)
    109 		return PLANE_Y;
    110 	if (normal[2] == 1.0 || normal[2] == -1.0)
    111 		return PLANE_Z;
    112 		
    113 	ax = fabs(normal[0]);
    114 	ay = fabs(normal[1]);
    115 	az = fabs(normal[2]);
    116 	
    117 	if (ax >= ay && ax >= az)
    118 		return PLANE_ANYX;
    119 	if (ay >= ax && ay >= az)
    120 		return PLANE_ANYY;
    121 	return PLANE_ANYZ;
    122 } //end of the function PlaneTypeForNormal
    123 //===========================================================================
    124 //
    125 // Parameter:				-
    126 // Returns:					-
    127 // Changes Globals:		-
    128 //===========================================================================
    129 //ME NOTE: changed from 0.00001
    130 #define	NORMAL_EPSILON	0.0001
    131 //ME NOTE: changed from 0.01
    132 #define	DIST_EPSILON	0.02
    133 qboolean	PlaneEqual(plane_t *p, vec3_t normal, vec_t dist)
    134 {
    135 #if 1
    136 	if (
    137 	   fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
    138 	&& fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
    139 	&& fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
    140 	&& fabs(p->dist - dist) < DIST_EPSILON )
    141 		return true;
    142 #else
    143 	if (p->normal[0] == normal[0]
    144 		&& p->normal[1] == normal[1]
    145 		&& p->normal[2] == normal[2]
    146 		&& p->dist == dist)
    147 		return true;
    148 #endif
    149 	return false;
    150 } //end of the function PlaneEqual
    151 //===========================================================================
    152 //
    153 // Parameter:				-
    154 // Returns:					-
    155 // Changes Globals:		-
    156 //===========================================================================
    157 void AddPlaneToHash(plane_t *p)
    158 {
    159 	int		hash;
    160 
    161 	hash = (int)fabs(p->dist) / 8;
    162 	hash &= (PLANE_HASHES-1);
    163 
    164 	p->hash_chain = planehash[hash];
    165 	planehash[hash] = p;
    166 } //end of the function AddPlaneToHash
    167 //===========================================================================
    168 //
    169 // Parameter:				-
    170 // Returns:					-
    171 // Changes Globals:		-
    172 //===========================================================================
    173 int CreateNewFloatPlane (vec3_t normal, vec_t dist)
    174 {
    175 	plane_t	*p, temp;
    176 
    177 	if (VectorLength(normal) < 0.5)
    178 		Error ("FloatPlane: bad normal");
    179 	// create a new plane
    180 	if (nummapplanes+2 > MAX_MAPFILE_PLANES)
    181 		Error ("MAX_MAPFILE_PLANES");
    182 
    183 	p = &mapplanes[nummapplanes];
    184 	VectorCopy (normal, p->normal);
    185 	p->dist = dist;
    186 	p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
    187 	p->signbits = PlaneSignBits(p->normal);
    188 
    189 	VectorSubtract (vec3_origin, normal, (p+1)->normal);
    190 	(p+1)->dist = -dist;
    191 	(p+1)->signbits = PlaneSignBits((p+1)->normal);
    192 
    193 	nummapplanes += 2;
    194 
    195 	// allways put axial planes facing positive first
    196 	if (p->type < 3)
    197 	{
    198 		if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
    199 		{
    200 			// flip order
    201 			temp = *p;
    202 			*p = *(p+1);
    203 			*(p+1) = temp;
    204 
    205 			AddPlaneToHash (p);
    206 			AddPlaneToHash (p+1);
    207 			return nummapplanes - 1;
    208 		}
    209 	}
    210 
    211 	AddPlaneToHash (p);
    212 	AddPlaneToHash (p+1);
    213 	return nummapplanes - 2;
    214 } //end of the function CreateNewFloatPlane
    215 //===========================================================================
    216 //
    217 // Parameter:				-
    218 // Returns:					-
    219 // Changes Globals:		-
    220 //===========================================================================
    221 void SnapVector(vec3_t normal)
    222 {
    223 	int		i;
    224 
    225 	for (i=0 ; i<3 ; i++)
    226 	{
    227 		if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
    228 		{
    229 			VectorClear (normal);
    230 			normal[i] = 1;
    231 			break;
    232 		}
    233 		if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
    234 		{
    235 			VectorClear (normal);
    236 			normal[i] = -1;
    237 			break;
    238 		}
    239 	}
    240 } //end of the function SnapVector
    241 //===========================================================================
    242 //
    243 // Parameter:				-
    244 // Returns:					-
    245 // Changes Globals:		-
    246 //===========================================================================
    247 void SnapPlane(vec3_t normal, vec_t *dist)
    248 {
    249 	SnapVector(normal);
    250 
    251 	if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
    252 		*dist = Q_rint(*dist);
    253 } //end of the function SnapPlane
    254 //===========================================================================
    255 //
    256 // Parameter:				-
    257 // Returns:					-
    258 // Changes Globals:		-
    259 //===========================================================================
    260 #ifndef USE_HASHING
    261 int FindFloatPlane(vec3_t normal, vec_t dist)
    262 {
    263 	int i;
    264 	plane_t *p;
    265 
    266 	SnapPlane(normal, &dist);
    267 	for (i = 0, p = mapplanes; i < nummapplanes; i++, p++)
    268 	{
    269 		if (PlaneEqual (p, normal, dist))
    270 		{
    271 			mapplaneusers[i]++;
    272 			return i;
    273 		} //end if
    274 	} //end for
    275 	i = CreateNewFloatPlane (normal, dist);
    276 	mapplaneusers[i]++;
    277 	return i;
    278 } //end of the function FindFloatPlane
    279 #else
    280 int FindFloatPlane (vec3_t normal, vec_t dist)
    281 {
    282 	int i;
    283 	plane_t *p;
    284 	int hash, h;
    285 
    286 	SnapPlane (normal, &dist);
    287 	hash = (int)fabs(dist) / 8;
    288 	hash &= (PLANE_HASHES-1);
    289 
    290 	// search the border bins as well
    291 	for (i = -1; i <= 1; i++)
    292 	{
    293 		h = (hash+i)&(PLANE_HASHES-1);
    294 		for (p = planehash[h]; p; p = p->hash_chain)
    295 		{
    296 			if (PlaneEqual(p, normal, dist))
    297 			{
    298 				mapplaneusers[p-mapplanes]++;
    299 				return p - mapplanes;
    300 			} //end if
    301 		} //end for
    302 	} //end for
    303 	i = CreateNewFloatPlane (normal, dist);
    304 	mapplaneusers[i]++;
    305 	return i;
    306 } //end of the function FindFloatPlane
    307 #endif
    308 //===========================================================================
    309 //
    310 // Parameter:				-
    311 // Returns:					-
    312 // Changes Globals:		-
    313 //===========================================================================
    314 int PlaneFromPoints (int *p0, int *p1, int *p2)
    315 {
    316 	vec3_t	t1, t2, normal;
    317 	vec_t	dist;
    318 
    319 	VectorSubtract (p0, p1, t1);
    320 	VectorSubtract (p2, p1, t2);
    321 	CrossProduct (t1, t2, normal);
    322 	VectorNormalize (normal);
    323 
    324 	dist = DotProduct (p0, normal);
    325 
    326 	return FindFloatPlane (normal, dist);
    327 } //end of the function PlaneFromPoints
    328 //===========================================================================
    329 // Adds any additional planes necessary to allow the brush to be expanded
    330 // against axial bounding boxes
    331 //
    332 // Parameter:				-
    333 // Returns:					-
    334 // Changes Globals:		-
    335 //===========================================================================
    336 void AddBrushBevels (mapbrush_t *b)
    337 {
    338 	int		axis, dir;
    339 	int		i, j, k, l, order;
    340 	side_t	sidetemp;
    341 	brush_texture_t	tdtemp;
    342 #ifdef SIN
    343    textureref_t trtemp;
    344 #endif
    345 	side_t	*s, *s2;
    346 	vec3_t	normal;
    347 	float	dist;
    348 	winding_t	*w, *w2;
    349 	vec3_t	vec, vec2;
    350 	float	d;
    351 
    352 	//
    353 	// add the axial planes
    354 	//
    355 	order = 0;
    356 	for (axis=0 ; axis <3 ; axis++)
    357 	{
    358 		for (dir=-1 ; dir <= 1 ; dir+=2, order++)
    359 		{
    360 			// see if the plane is allready present
    361 			for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
    362 			{
    363 				if (mapplanes[s->planenum].normal[axis] == dir)
    364 					break;
    365 			}
    366 
    367 			if (i == b->numsides)
    368 			{	// add a new side
    369 				if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
    370 					Error ("MAX_MAP_BRUSHSIDES");
    371 				nummapbrushsides++;
    372 				b->numsides++;
    373 				VectorClear (normal);
    374 				normal[axis] = dir;
    375 				if (dir == 1)
    376 					dist = b->maxs[axis];
    377 				else
    378 					dist = -b->mins[axis];
    379 				s->planenum = FindFloatPlane (normal, dist);
    380 				s->texinfo = b->original_sides[0].texinfo;
    381 #ifdef SIN
    382 				s->lightinfo = b->original_sides[0].lightinfo;
    383 #endif
    384 				s->contents = b->original_sides[0].contents;
    385 				s->flags |= SFL_BEVEL;
    386 				c_boxbevels++;
    387 			}
    388 
    389 			// if the plane is not in it canonical order, swap it
    390 			if (i != order)
    391 			{
    392 				sidetemp = b->original_sides[order];
    393 				b->original_sides[order] = b->original_sides[i];
    394 				b->original_sides[i] = sidetemp;
    395 
    396 				j = b->original_sides - brushsides;
    397 				tdtemp = side_brushtextures[j+order];
    398 				side_brushtextures[j+order] = side_brushtextures[j+i];
    399 				side_brushtextures[j+i] = tdtemp;
    400 
    401 #ifdef SIN
    402 				trtemp = side_newrefs[j+order];
    403 				side_newrefs[j+order] = side_newrefs[j+i];
    404 				side_newrefs[j+i] = trtemp;
    405 #endif
    406 			}
    407 		}
    408 	}
    409 
    410 	//
    411 	// add the edge bevels
    412 	//
    413 	if (b->numsides == 6)
    414 		return;		// pure axial
    415 
    416 	// test the non-axial plane edges
    417 	for (i=6 ; i<b->numsides ; i++)
    418 	{
    419 		s = b->original_sides + i;
    420 		w = s->winding;
    421 		if (!w)
    422 			continue;
    423 		for (j=0 ; j<w->numpoints ; j++)
    424 		{
    425 			k = (j+1)%w->numpoints;
    426 			VectorSubtract (w->p[j], w->p[k], vec);
    427 			if (VectorNormalize (vec) < 0.5)
    428 				continue;
    429 			SnapVector (vec);
    430 			for (k=0 ; k<3 ; k++)
    431 				if ( vec[k] == -1 || vec[k] == 1)
    432 					break;	// axial
    433 			if (k != 3)
    434 				continue;	// only test non-axial edges
    435 
    436 			// try the six possible slanted axials from this edge
    437 			for (axis=0 ; axis <3 ; axis++)
    438 			{
    439 				for (dir=-1 ; dir <= 1 ; dir+=2)
    440 				{
    441 					// construct a plane
    442 					VectorClear (vec2);
    443 					vec2[axis] = dir;
    444 					CrossProduct (vec, vec2, normal);
    445 					if (VectorNormalize (normal) < 0.5)
    446 						continue;
    447 					dist = DotProduct (w->p[j], normal);
    448 
    449 					// if all the points on all the sides are
    450 					// behind this plane, it is a proper edge bevel
    451 					for (k=0 ; k<b->numsides ; k++)
    452 					{
    453 						// if this plane has allready been used, skip it
    454 						if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
    455 							, normal, dist) )
    456 							break;
    457 
    458 						w2 = b->original_sides[k].winding;
    459 						if (!w2)
    460 							continue;
    461 						for (l=0 ; l<w2->numpoints ; l++)
    462 						{
    463 							d = DotProduct (w2->p[l], normal) - dist;
    464 							if (d > 0.1)
    465 								break;	// point in front
    466 						}
    467 						if (l != w2->numpoints)
    468 							break;
    469 					}
    470 
    471 					if (k != b->numsides)
    472 						continue;	// wasn't part of the outer hull
    473 					// add this plane
    474 					if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
    475 						Error ("MAX_MAP_BRUSHSIDES");
    476 					nummapbrushsides++;
    477 					s2 = &b->original_sides[b->numsides];
    478 					s2->planenum = FindFloatPlane (normal, dist);
    479 					s2->texinfo = b->original_sides[0].texinfo;
    480 #ifdef SIN
    481 					s2->lightinfo = b->original_sides[0].lightinfo;
    482 #endif
    483 					s2->contents = b->original_sides[0].contents;
    484 					s2->flags |= SFL_BEVEL;
    485 					c_edgebevels++;
    486 					b->numsides++;
    487 				}
    488 			}
    489 		}
    490 	}
    491 } //end of the function AddBrushBevels
    492 //===========================================================================
    493 // creates windigs for sides and mins / maxs for the brush
    494 //
    495 // Parameter:				-
    496 // Returns:					-
    497 // Changes Globals:		-
    498 //===========================================================================
    499 qboolean MakeBrushWindings(mapbrush_t *ob)
    500 {
    501 	int			i, j;
    502 	winding_t	*w;
    503 	side_t		*side;
    504 	plane_t		*plane;
    505 
    506 	ClearBounds (ob->mins, ob->maxs);
    507 
    508 	for (i = 0; i < ob->numsides; i++)
    509 	{
    510 		plane = &mapplanes[ob->original_sides[i].planenum];
    511 		w = BaseWindingForPlane(plane->normal, plane->dist);
    512 		for (j = 0; j <ob->numsides && w; j++)
    513 		{
    514 			if (i == j) continue;
    515 			if (ob->original_sides[j].flags & SFL_BEVEL) continue;
    516 			plane = &mapplanes[ob->original_sides[j].planenum^1];
    517 			ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
    518 		}
    519 
    520 		side = &ob->original_sides[i];
    521 		side->winding = w;
    522 		if (w)
    523 		{
    524 			side->flags |= SFL_VISIBLE;
    525 			for (j = 0; j < w->numpoints; j++)
    526 				AddPointToBounds (w->p[j], ob->mins, ob->maxs);
    527 		}
    528 	}
    529 
    530 	for (i = 0; i < 3; i++)
    531 	{
    532 		//IDBUG: all the indexes into the mins and maxs were zero (not using i)
    533 		if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS)
    534 		{
    535 			Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
    536 			ob->numsides = 0; //remove the brush
    537 			break;
    538 		} //end if
    539 		if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS)
    540 		{
    541 			Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
    542 			ob->numsides = 0; //remove the brush
    543 			break;
    544 		} //end if
    545 	} //end for
    546 	return true;
    547 } //end of the function MakeBrushWindings
    548 //===========================================================================
    549 // FIXME: currently doesn't mark all bevels
    550 // NOTE: when one brush bevel is found the remaining sides of the brush
    551 //       are bevels as well (when the brush isn't expanded for AAS :))
    552 //
    553 // Parameter:				-
    554 // Returns:					-
    555 // Changes Globals:		-
    556 //===========================================================================
    557 void MarkBrushBevels(mapbrush_t *brush)
    558 {
    559 	int i;
    560 	int we;
    561 	side_t *s;
    562 
    563 	//check all the sides of the brush
    564 	for (i = 0; i < brush->numsides; i++)
    565 	{
    566 		s = brush->original_sides + i;
    567 		//if the side has no winding
    568 		if (!s->winding)
    569 		{
    570 			Log_Write("MarkBrushBevels: brush %d no winding", brush->brushnum);
    571 			s->flags |= SFL_BEVEL;
    572 		} //end if
    573 		//if the winding is tiny
    574 		else if (WindingIsTiny(s->winding))
    575 		{
    576 			s->flags |= SFL_BEVEL;
    577 			Log_Write("MarkBrushBevels: brush %d tiny winding", brush->brushnum);
    578 		} //end else if
    579 		//if the winding has errors
    580 		else
    581 		{
    582 			we = WindingError(s->winding);
    583 			if (we == WE_NOTENOUGHPOINTS
    584 					|| we == WE_SMALLAREA
    585 					|| we == WE_POINTBOGUSRANGE
    586 //					|| we == WE_NONCONVEX
    587 					)
    588 			{
    589 				Log_Write("MarkBrushBevels: brush %d %s", brush->brushnum, WindingErrorString());
    590 				s->flags |= SFL_BEVEL;
    591 			} //end else if
    592 		} //end else
    593 		if (s->flags & SFL_BEVEL)
    594 		{
    595 			s->flags &= ~SFL_VISIBLE;
    596 			//if the side has a valid plane
    597 			if (s->planenum > 0 && s->planenum < nummapplanes)
    598 			{
    599 				//if it is an axial plane
    600 				if (mapplanes[s->planenum].type < 3) c_boxbevels++;
    601 				else c_edgebevels++;
    602 			} //end if
    603 		} //end if
    604 	} //end for
    605 } //end of the function MarkBrushBevels
    606 //===========================================================================
    607 // returns true if the map brush already exists
    608 //
    609 // Parameter:				-
    610 // Returns:					-
    611 // Changes Globals:		-
    612 //===========================================================================
    613 int BrushExists(mapbrush_t *brush)
    614 {
    615 	int i, s1, s2;
    616 	side_t *side1, *side2;
    617 	mapbrush_t *brush1, *brush2;
    618 
    619 	for (i = 0; i < nummapbrushes; i++)
    620 	{
    621 		brush1 = brush;
    622 		brush2 = &mapbrushes[i];
    623 		//compare the brushes
    624 		if (brush1->entitynum != brush2->entitynum) continue;
    625 		//if (brush1->contents != brush2->contents) continue;
    626 		if (brush1->numsides != brush2->numsides) continue;
    627 		for (s1 = 0; s1 < brush1->numsides; s1++)
    628 		{
    629 			side1 = brush1->original_sides + s1;
    630 			//
    631 			for (s2 = 0; s2 < brush2->numsides; s2++)
    632 			{
    633 				side2 = brush2->original_sides + s2;
    634 				//
    635 				if ((side1->planenum & ~1) == (side2->planenum & ~1)
    636 //						&& side1->texinfo == side2->texinfo
    637 //						&& side1->contents == side2->contents
    638 //						&& side1->surf == side2->surf
    639 					) break;
    640 			} //end if
    641 			if (s2 >= brush2->numsides) break;
    642 		} //end for
    643 		if (s1 >= brush1->numsides) return true;
    644 	} //end for
    645 	return false;
    646 } //end of the function BrushExists
    647 //===========================================================================
    648 //
    649 // Parameter:				-
    650 // Returns:					-
    651 // Changes Globals:		-
    652 //===========================================================================
    653 qboolean WriteMapBrush(FILE *fp, mapbrush_t *brush, vec3_t origin)
    654 {
    655 	int sn, rotate, shift[2], sv, tv, planenum, p1, i, j;
    656 	float scale[2], originshift[2], ang1, ang2, newdist;
    657 	vec3_t vecs[2], axis[2];
    658 	map_texinfo_t *ti;
    659 	winding_t *w;
    660 	side_t *s;
    661 	plane_t *plane;
    662 
    663 	if (noliquids)
    664 	{
    665 		if (brush->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))
    666 		{
    667 			return true;
    668 		} //end if
    669 	} //end if
    670 	//if the brush has no contents
    671 	if (!brush->contents) return true;
    672 	//print the leading {
    673 	if (fprintf(fp, " { //brush %d\n", brush->brushnum) < 0) return false;
    674 	//write brush sides
    675 	for (sn = 0; sn < brush->numsides; sn++)
    676 	{
    677 		s = brush->original_sides + sn;
    678 		//don't write out bevels
    679 		if (!(s->flags & SFL_BEVEL))
    680 		{
    681 			//if the entity has an origin set
    682 			if (origin[0] || origin[1] || origin[2])
    683 			{
    684 				newdist = mapplanes[s->planenum].dist +
    685 					DotProduct(mapplanes[s->planenum].normal, origin);
    686 				planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist);
    687 			} //end if
    688 			else
    689 			{
    690 				planenum = s->planenum;
    691 			} //end else
    692 			//always take the first plane, then flip the points if necesary
    693 			plane = &mapplanes[planenum & ~1];
    694 			w = BaseWindingForPlane(plane->normal, plane->dist);
    695 			//
    696 			for (i = 0; i < 3; i++)
    697 			{
    698 				for (j = 0; j < 3; j++)
    699 				{
    700 					if (fabs(w->p[i][j]) < 0.2) w->p[i][j] = 0;
    701 					else if (fabs((int)w->p[i][j] - w->p[i][j]) < 0.3) w->p[i][j] = (int) w->p[i][j];
    702 					//w->p[i][j] = (int) (w->p[i][j] + 0.2);
    703 				} //end for
    704 			} //end for
    705 			//three non-colinear points to define the plane
    706 			if (planenum & 1) p1 = 1;
    707 			else p1 = 0;
    708 			if (fprintf(fp,"  ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2]) < 0) return false;
    709 			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2]) < 0) return false;
    710 			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
    711 			//free the winding
    712 			FreeWinding(w);
    713 			//
    714 			if (s->texinfo == TEXINFO_NODE)
    715 			{
    716 				if (brush->contents & CONTENTS_PLAYERCLIP)
    717 				{
    718 					//player clip
    719 					if (loadedmaptype == MAPTYPE_SIN)
    720 					{
    721 						if (fprintf(fp, "generic/misc/clip 0 0 0 1 1") < 0) return false;
    722 					} //end if
    723 					else if (loadedmaptype == MAPTYPE_QUAKE2)
    724 					{	//FIXME: don't always use e1u1
    725 						if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
    726 					} //end else
    727 					else if (loadedmaptype == MAPTYPE_QUAKE3)
    728 					{
    729 						if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
    730 					} //end else if
    731 					else
    732 					{
    733 						if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
    734 					} //end else
    735 				} //end if
    736 				else if (brush->contents == CONTENTS_MONSTERCLIP)
    737 				{
    738 					//monster clip
    739 					if (loadedmaptype == MAPTYPE_SIN)
    740 					{
    741 						if (fprintf(fp, "generic/misc/monster 0 0 0 1 1") < 0) return false;
    742 					} //end if
    743 					else if (loadedmaptype == MAPTYPE_QUAKE2)
    744 					{
    745 						if (fprintf(fp, "e1u1/clip_mon 0 0 0 1 1") < 0) return false;
    746 					} //end else
    747 					else
    748 					{
    749 						if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
    750 					} //end else
    751 				} //end else
    752 				else
    753 				{
    754 					if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
    755 					Log_Write("brush->contents = %d\n", brush->contents);
    756 				} //end else
    757 			} //end if
    758 			else if (loadedmaptype == MAPTYPE_SIN && s->texinfo == 0)
    759 			{
    760 				if (brush->contents & CONTENTS_DUMMYFENCE)
    761 				{
    762 					if (fprintf(fp, "generic/misc/fence 0 0 0 1 1") < 0) return false;
    763 				} //end if
    764 				else if (brush->contents & CONTENTS_MIST)
    765 				{
    766 					if (fprintf(fp, "generic/misc/volumetric_base 0 0 0 1 1") < 0) return false;
    767 				} //end if
    768 				else //unknown so far
    769 				{
    770 					if (fprintf(fp, "generic/misc/red 0 0 0 1 1") < 0) return false;
    771 				} //end else
    772 			} //end if
    773 			else if (loadedmaptype == MAPTYPE_QUAKE3)
    774 			{
    775 				//always use the same texture
    776 				if (fprintf(fp, "e2u3/floor1_2 0 0 0 1 1 1 0 0") < 0) return false;
    777 			} //end else if
    778 			else
    779 			{
    780 				//*
    781 				ti = &map_texinfo[s->texinfo];
    782 				//the scaling of the texture
    783 				scale[0] = 1 / VectorNormalize2(ti->vecs[0], vecs[0]);
    784 				scale[1] = 1 / VectorNormalize2(ti->vecs[1], vecs[1]);
    785 				//
    786 				TextureAxisFromPlane(plane, axis[0], axis[1]);
    787 				//calculate texture shift done by entity origin
    788 				originshift[0] = DotProduct(origin, axis[0]);
    789 				originshift[1] = DotProduct(origin, axis[1]);
    790 				//the texture shift without origin shift
    791 				shift[0] = ti->vecs[0][3] - originshift[0];
    792 				shift[1] = ti->vecs[1][3] - originshift[1];
    793 				//
    794 				if (axis[0][0]) sv = 0;
    795 				else if (axis[0][1]) sv = 1;
    796 				else sv = 2;
    797 				if (axis[1][0]) tv = 0;
    798 				else if (axis[1][1]) tv = 1;
    799 				else tv = 2;
    800 				//calculate rotation of texture
    801 				if (vecs[0][tv] == 0) ang1 = vecs[0][sv] > 0 ? 90.0 : -90.0;
    802 				else ang1 = atan2(vecs[0][sv], vecs[0][tv]) * 180 / Q_PI;
    803 				if (ang1 < 0) ang1 += 360;
    804 				if (ang1 >= 360) ang1 -= 360;
    805 				if (axis[0][tv] == 0) ang2 = axis[0][sv] > 0 ? 90.0 : -90.0;
    806 				else ang2 = atan2(axis[0][sv], axis[0][tv]) * 180 / Q_PI;
    807 				if (ang2 < 0) ang2 += 360;
    808 				if (ang2 >= 360) ang2 -= 360;
    809 				rotate = ang2 - ang1;
    810 				if (rotate < 0) rotate += 360;
    811 				if (rotate >= 360) rotate -= 360;
    812 				//write the texture info
    813 				if (fprintf(fp, "%s %d %d %d", ti->texture, shift[0], shift[1], rotate) < 0) return false;
    814 				if (fabs(scale[0] - ((int) scale[0])) < 0.001)
    815 				{
    816 					if (fprintf(fp, " %d", (int) scale[0]) < 0) return false;
    817 				} //end if
    818 				else
    819 				{
    820 					if (fprintf(fp, " %4f", scale[0]) < 0) return false;
    821 				} //end if
    822 				if (fabs(scale[1] - ((int) scale[1])) < 0.001)
    823 				{
    824 					if (fprintf(fp, " %d", (int) scale[1]) < 0) return false;
    825 				} //end if
    826 				else
    827 				{
    828 					if (fprintf(fp, " %4f", scale[1]) < 0) return false;
    829 				} //end else
    830 				//write the extra brush side info
    831 				if (loadedmaptype == MAPTYPE_QUAKE2)
    832 				{
    833 					if (fprintf(fp, " %ld %ld %ld", s->contents, ti->flags, ti->value) < 0) return false;
    834 				} //end if
    835 				//*/
    836 			} //end else
    837 			if (fprintf(fp, "\n") < 0) return false;
    838 		} //end if
    839 	} //end if
    840 	if (fprintf(fp, " }\n") < 0) return false;
    841 	c_writtenbrushes++;
    842 	return true;
    843 } //end of the function WriteMapBrush
    844 //===========================================================================
    845 //
    846 // Parameter:				-
    847 // Returns:					-
    848 // Changes Globals:		-
    849 //===========================================================================
    850 qboolean WriteOriginBrush(FILE *fp, vec3_t origin)
    851 {
    852 	vec3_t normal;
    853 	float dist;
    854 	int i, s;
    855 	winding_t *w;
    856 
    857 	if (fprintf(fp, " {\n") < 0) return false;
    858 	//
    859 	for (i = 0; i < 3; i++)
    860 	{
    861 		for (s = -1; s <= 1; s += 2)
    862 		{
    863 			//
    864 			VectorClear(normal);
    865 			normal[i] = s;
    866 			dist = origin[i] * s + 16;
    867 			//
    868 			w = BaseWindingForPlane(normal, dist);
    869 			//three non-colinear points to define the plane
    870 			if (fprintf(fp,"  ( %5i %5i %5i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]) < 0) return false;
    871 			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]) < 0) return false;
    872 			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
    873 			//free the winding
    874 			FreeWinding(w);
    875 			//write origin texture:
    876 			// CONTENTS_ORIGIN = 16777216
    877 			// SURF_NODRAW = 128
    878 			if (loadedmaptype == MAPTYPE_SIN)
    879 			{
    880 				if (fprintf(fp, "generic/misc/origin 0 0 0 1 1") < 0) return false;
    881 			} //end if
    882 			else if (loadedmaptype == MAPTYPE_HALFLIFE)
    883 			{
    884 				if (fprintf(fp, "origin 0 0 0 1 1") < 0) return false;
    885 			} //end if
    886 			else
    887 			{
    888 				if (fprintf(fp, "e1u1/origin 0 0 0 1 1") < 0) return false;
    889 			} //end else
    890 			//Quake2 extra brush side info
    891 			if (loadedmaptype == MAPTYPE_QUAKE2)
    892 			{
    893 				//if (fprintf(fp, " 16777216 128 0") < 0) return false;
    894 			} //end if
    895 			if (fprintf(fp, "\n") < 0) return false;
    896 		} //end for
    897 	} //end for
    898 	if (fprintf(fp, " }\n") < 0) return false;
    899 	c_writtenbrushes++;
    900 	return true;
    901 } //end of the function WriteOriginBrush
    902 //===========================================================================
    903 //
    904 // Parameter:				-
    905 // Returns:					-
    906 // Changes Globals:		-
    907 //===========================================================================
    908 mapbrush_t *GetAreaPortalBrush(entity_t *mapent)
    909 {
    910 	int portalnum, bn;
    911 	mapbrush_t *brush;
    912 
    913 	//the area portal number
    914 	portalnum = mapent->areaportalnum;
    915 	//find the area portal brush in the world brushes
    916 	for (bn = 0; bn < nummapbrushes && portalnum; bn++)
    917 	{
    918 		brush = &mapbrushes[bn];
    919 		//must be in world entity
    920 		if (brush->entitynum == 0)
    921 		{
    922 			if (brush->contents & CONTENTS_AREAPORTAL)
    923 			{
    924 				portalnum--;
    925 			} //end if
    926 		} //end if
    927 	} //end for
    928 	if (bn < nummapbrushes)
    929 	{
    930 		return brush;
    931 	} //end if
    932 	else
    933 	{
    934 		Log_Print("area portal %d brush not found\n", mapent->areaportalnum);
    935 		return NULL;
    936 	} //end else
    937 } //end of the function GetAreaPortalBrush
    938 //===========================================================================
    939 //
    940 // Parameter:				-
    941 // Returns:					-
    942 // Changes Globals:		-
    943 //===========================================================================
    944 qboolean WriteMapFileSafe(FILE *fp)
    945 {
    946 	char key[1024], value[1024];
    947 	int i, bn, entitybrushes;
    948 	epair_t *ep;
    949 	mapbrush_t *brush;
    950 	entity_t *mapent;
    951 	//vec3_t vec_origin = {0, 0, 0};
    952 
    953 	//
    954 	if (fprintf(fp,"//=====================================================\n"
    955 			"//\n"
    956 			"// map file created with BSPC "BSPC_VERSION"\n"
    957 			"//\n"
    958 			"// BSPC is designed to decompile material in which you own the copyright\n"
    959 			"// or have obtained permission to decompile from the copyright owner. Unless\n"
    960 			"// you own the copyright or have permission to decompile from the copyright\n"
    961 			"// owner, you may be violating copyright law and be subject to payment of\n"
    962 			"// damages and other remedies. If you are uncertain about your rights, contact\n"
    963 			"// your legal advisor.\n"
    964 			"//\n") < 0) return false;
    965 	if (loadedmaptype == MAPTYPE_SIN)
    966 	{
    967 		if (fprintf(fp,
    968 						"// generic/misc/red is used for unknown textures\n") < 0) return false;
    969 	} //end if
    970 	if (fprintf(fp,"//\n"
    971 						"//=====================================================\n") < 0) return false;
    972 	//write out all the entities
    973 	for (i = 0; i < num_entities; i++)
    974 	{
    975 		mapent = &entities[i];
    976 		if (!mapent->epairs)
    977 		{
    978 			continue;
    979 		} //end if
    980 		if (fprintf(fp, "{\n") < 0) return false;
    981 		//
    982 		if (loadedmaptype == MAPTYPE_QUAKE3)
    983 		{
    984 			if (!stricmp(ValueForKey(mapent, "classname"), "light"))
    985 			{
    986 				SetKeyValue(mapent, "light", "10000");
    987 			} //end if
    988 		} //end if
    989 		//write epairs
    990 		for (ep = mapent->epairs; ep; ep = ep->next)
    991 		{
    992 			strcpy(key, ep->key);
    993 			StripTrailing (key);
    994 			strcpy(value, ep->value);
    995 			StripTrailing(value);
    996 			//
    997 			if (loadedmaptype == MAPTYPE_QUAKE2 ||
    998 					loadedmaptype == MAPTYPE_SIN)
    999 			{
   1000 				//don't write an origin for BSP models
   1001 				if (mapent->modelnum >= 0 && !strcmp(key, "origin")) continue;
   1002 			} //end if
   1003 			//don't write BSP model numbers
   1004 			if (mapent->modelnum >= 0 && !strcmp(key, "model") && value[0] == '*') continue;
   1005 			//
   1006 			if (fprintf(fp, " \"%s\" \"%s\"\n", key, value) < 0) return false;
   1007 		} //end for
   1008 		//
   1009 		if (ValueForKey(mapent, "origin")) GetVectorForKey(mapent, "origin", mapent->origin);
   1010 		else mapent->origin[0] = mapent->origin[1] = mapent->origin[2] = 0;
   1011 		//if this is an area portal entity
   1012 		if (!strcmp("func_areaportal", ValueForKey(mapent, "classname")))
   1013 		{
   1014 			brush = GetAreaPortalBrush(mapent);
   1015 			if (!brush) return false;
   1016 			if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
   1017 		} //end if
   1018 		else
   1019 		{
   1020 			entitybrushes = false;
   1021 			//write brushes
   1022 			for (bn = 0; bn < nummapbrushes; bn++)
   1023 			{
   1024 				brush = &mapbrushes[bn];
   1025 				//if the brush is part of this entity
   1026 				if (brush->entitynum == i)
   1027 				{
   1028 					//don't write out area portal brushes in the world
   1029 					if (!((brush->contents & CONTENTS_AREAPORTAL) && brush->entitynum == 0))
   1030 					{
   1031 						/*
   1032 						if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname")))
   1033 						{
   1034 							AAS_PositionFuncRotatingBrush(mapent, brush);
   1035 							if (!WriteMapBrush(fp, brush, vec_origin)) return false;
   1036 						} //end if
   1037 						else //*/
   1038 						{
   1039 							if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
   1040 						} //end else
   1041 						entitybrushes = true;
   1042 					} //end if
   1043 				} //end if
   1044 			} //end for
   1045 			//if the entity had brushes
   1046 			if (entitybrushes)
   1047 			{
   1048 				//if the entity has an origin set
   1049 				if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
   1050 				{
   1051 					if (!WriteOriginBrush(fp, mapent->origin)) return false;
   1052 				} //end if
   1053 			} //end if
   1054 		} //end else
   1055 		if (fprintf(fp, "}\n") < 0) return false;
   1056 	} //end for
   1057 	if (fprintf(fp, "//total of %d brushes\n", c_writtenbrushes) < 0) return false;
   1058 	return true;
   1059 } //end of the function WriteMapFileSafe
   1060 //===========================================================================
   1061 //
   1062 // Parameter:				-
   1063 // Returns:					-
   1064 // Changes Globals:		-
   1065 //===========================================================================
   1066 void WriteMapFile(char *filename)
   1067 {
   1068 	FILE *fp;
   1069 	double start_time;
   1070 
   1071 	c_writtenbrushes = 0;
   1072 	//the time started
   1073 	start_time = I_FloatTime();
   1074 	//
   1075 	Log_Print("writing %s\n", filename);
   1076 	fp = fopen(filename, "wb");
   1077 	if (!fp)
   1078 	{
   1079 		Log_Print("can't open %s\n", filename);
   1080 		return;
   1081 	} //end if
   1082 	if (!WriteMapFileSafe(fp))
   1083 	{
   1084 		fclose(fp);
   1085 		Log_Print("error writing map file %s\n", filename);
   1086 		return;
   1087 	} //end if
   1088 	fclose(fp);
   1089 	//display creation time
   1090 	Log_Print("written %d brushes\n", c_writtenbrushes);
   1091 	Log_Print("map file written in %5.0f seconds\n", I_FloatTime() - start_time);
   1092 } //end of the function WriteMapFile
   1093 //===========================================================================
   1094 //
   1095 // Parameter:				-
   1096 // Returns:					-
   1097 // Changes Globals:		-
   1098 //===========================================================================
   1099 void PrintMapInfo(void)
   1100 {
   1101 	Log_Print("\n");
   1102 	Log_Print("%6i brushes\n", nummapbrushes);
   1103 	Log_Print("%6i brush sides\n", nummapbrushsides);
   1104 //	Log_Print("%6i clipbrushes\n", c_clipbrushes);
   1105 //	Log_Print("%6i total sides\n", nummapbrushsides);
   1106 //	Log_Print("%6i boxbevels\n", c_boxbevels);
   1107 //	Log_Print("%6i edgebevels\n", c_edgebevels);
   1108 //	Log_Print("%6i entities\n", num_entities);
   1109 //	Log_Print("%6i planes\n", nummapplanes);
   1110 //	Log_Print("%6i areaportals\n", c_areaportals);
   1111 //	Log_Print("%6i squatt brushes\n", c_squattbrushes);
   1112 //	Log_Print("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
   1113 //		map_maxs[0],map_maxs[1],map_maxs[2]);
   1114 } //end of the function PrintMapInfo
   1115 //===========================================================================
   1116 //
   1117 // Parameter:				-
   1118 // Returns:					-
   1119 // Changes Globals:		-
   1120 //===========================================================================
   1121 void ResetMapLoading(void)
   1122 {
   1123 	int i;
   1124 	epair_t *ep, *nextep;
   1125 
   1126 	Q2_ResetMapLoading();
   1127 	Sin_ResetMapLoading();
   1128 
   1129 	//free all map brush side windings
   1130 	for (i = 0; i < nummapbrushsides; i++)
   1131 	{
   1132 		if (brushsides[i].winding)
   1133 		{
   1134 			FreeWinding(brushsides[i].winding);
   1135 		} //end for
   1136 	} //end for
   1137 
   1138 	//reset regular stuff
   1139 	nummapbrushes = 0;
   1140 	memset(mapbrushes, 0, MAX_MAPFILE_BRUSHES * sizeof(mapbrush_t));
   1141 	//
   1142 	nummapbrushsides = 0;
   1143 	memset(brushsides, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(side_t));
   1144 	memset(side_brushtextures, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(brush_texture_t));
   1145 	//
   1146 	nummapplanes = 0;
   1147 	memset(mapplanes, 0, MAX_MAPFILE_PLANES * sizeof(plane_t));
   1148 	//
   1149 	memset(planehash, 0, PLANE_HASHES * sizeof(plane_t *));
   1150 	//
   1151 	memset(map_texinfo, 0, MAX_MAPFILE_TEXINFO * sizeof(map_texinfo_t));
   1152 	map_numtexinfo = 0;
   1153 	//
   1154 	VectorClear(map_mins);
   1155 	VectorClear(map_maxs);
   1156 	//
   1157 	c_boxbevels = 0;
   1158 	c_edgebevels = 0;
   1159 	c_areaportals = 0;
   1160 	c_clipbrushes = 0;
   1161 	c_writtenbrushes = 0;
   1162 	//clear the entities
   1163 	for (i = 0; i < num_entities; i++)
   1164 	{
   1165 		for (ep = entities[i].epairs; ep; ep = nextep)
   1166 		{
   1167 			nextep = ep->next;
   1168 			FreeMemory(ep->key);
   1169 			FreeMemory(ep->value);
   1170 			FreeMemory(ep);
   1171 		} //end for
   1172 	} //end for
   1173 	num_entities = 0;
   1174 	memset(entities, 0, MAX_MAP_ENTITIES * sizeof(entity_t));
   1175 } //end of the function ResetMapLoading
   1176 //===========================================================================
   1177 //
   1178 // Parameter:				-
   1179 // Returns:					-
   1180 // Changes Globals:		-
   1181 //===========================================================================
   1182 #ifndef Q1_BSPVERSION
   1183 #define Q1_BSPVERSION	29
   1184 #endif
   1185 #ifndef HL_BSPVERSION
   1186 #define HL_BSPVERSION	30
   1187 #endif
   1188 
   1189 #define Q2_BSPHEADER				(('P'<<24)+('S'<<16)+('B'<<8)+'I')	//IBSP
   1190 #define Q2_BSPVERSION	38
   1191 
   1192 #define SINGAME_BSPHEADER		(('P'<<24)+('S'<<16)+('B'<<8)+'R')	//RBSP
   1193 #define SINGAME_BSPVERSION		1
   1194 
   1195 #define SIN_BSPHEADER			(('P'<<24)+('S'<<16)+('B'<<8)+'I')	//IBSP
   1196 #define SIN_BSPVERSION	41
   1197 
   1198 typedef struct
   1199 {
   1200 	int ident;
   1201 	int version;
   1202 } idheader_t;
   1203 
   1204 int LoadMapFromBSP(struct quakefile_s *qf)
   1205 {
   1206 	idheader_t idheader;
   1207 
   1208 	if (ReadQuakeFile(qf, &idheader, 0, sizeof(idheader_t)) != sizeof(idheader_t))
   1209 	{
   1210 		return false;
   1211 	} //end if
   1212 
   1213 	idheader.ident = LittleLong(idheader.ident);
   1214 	idheader.version = LittleLong(idheader.version);
   1215 	//Quake3 BSP file
   1216 	if (idheader.ident == Q3_BSP_IDENT && idheader.version == Q3_BSP_VERSION)
   1217 	{
   1218 		ResetMapLoading();
   1219 		Q3_LoadMapFromBSP(qf);
   1220 		Q3_FreeMaxBSP();
   1221 	} //end if
   1222 	//Quake2 BSP file
   1223 	else if (idheader.ident == Q2_BSPHEADER && idheader.version == Q2_BSPVERSION)
   1224 	{
   1225 		ResetMapLoading();
   1226 		Q2_AllocMaxBSP();
   1227 		Q2_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
   1228 		Q2_FreeMaxBSP();
   1229 	} //endif
   1230 	//Sin BSP file
   1231 	else if ((idheader.ident == SIN_BSPHEADER && idheader.version == SIN_BSPVERSION) ||
   1232 				//the dorks gave the same format another ident and verions
   1233 				(idheader.ident == SINGAME_BSPHEADER && idheader.version == SINGAME_BSPVERSION))
   1234 	{
   1235 		ResetMapLoading();
   1236 		Sin_AllocMaxBSP();
   1237 		Sin_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
   1238 		Sin_FreeMaxBSP();
   1239 	} //end if
   1240 	//the Quake1 bsp files don't have a ident only a version
   1241 	else if (idheader.ident == Q1_BSPVERSION)
   1242 	{
   1243 		ResetMapLoading();
   1244 		Q1_AllocMaxBSP();
   1245 		Q1_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
   1246 		Q1_FreeMaxBSP();
   1247 	} //end if
   1248 	//Half-Life also only uses a version number
   1249 	else if (idheader.ident == HL_BSPVERSION)
   1250 	{
   1251 		ResetMapLoading();
   1252 		HL_AllocMaxBSP();
   1253 		HL_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
   1254 		HL_FreeMaxBSP();
   1255 	} //end if
   1256 	else
   1257 	{
   1258 		Error("unknown BSP format %c%c%c%c, version %d\n",
   1259 										(idheader.ident & 0xFF),
   1260 										((idheader.ident >> 8) & 0xFF),
   1261 										((idheader.ident >> 16) & 0xFF),
   1262 										((idheader.ident >> 24) & 0xFF), idheader.version);
   1263 		return false;
   1264 	} //end if
   1265 	//
   1266 	return true;
   1267 } //end of the function LoadMapFromBSP