
#include "bsp5.h"

// =============================================================================

int nummodels;
SetupArray( dmodel_t, dmodels, MAX_MAP_MODELS );

int visdatasize;
SetupArray( byte, dvisdata, MAX_MAP_VISIBILITY );

int lightdatasize;
SetupArray( byte, dlightdata, MAX_MAP_LIGHTING );

int texdatasize;
SetupArray( byte, dtexdata, MAX_MAP_MIPTEX ); // (dmiptexlump_t)

int entdatasize;
SetupArray( char, dentdata, MAX_MAP_ENTSTRING );

int numleafs;
SetupArray( dleaf_t, dleafs, MAX_MAP_LEAFS );

int numplanes;
SetupArray( dplane_t, dplanes, MAX_MAP_PLANES );

int numvertexes;
SetupArray( dvertex_t, dvertexes, MAX_MAP_VERTS );

int numnodes;
SetupArray( dnode_t, dnodes, MAX_MAP_NODES );

int numtexinfo;
SetupArray( texinfo_t, texinfo, MAX_MAP_TEXINFO );

int numfaces;
SetupArray( dface_t, dfaces, MAX_MAP_FACES );

int numclipnodes;
SetupArray( dclipnode_t, dclipnodes, MAX_MAP_CLIPNODES );

int numedges;
SetupArray( dedge_t, dedges, MAX_MAP_EDGES );

int nummarksurfaces;
SetupArray( unsigned short, dmarksurfaces, MAX_MAP_MARKSURFACES );

int numsurfedges;
SetupArray( int, dsurfedges, MAX_MAP_SURFEDGES );

// BSP2 level data structures
SetupArray( dleafBSP2_t, dleafsBSP2, MAX_MAP_LEAFS );
SetupArray( dnodeBSP2_t, dnodesBSP2, MAX_MAP_NODES );
SetupArray( dfaceBSP2_t, dfacesBSP2, MAX_MAP_FACES );
SetupArray( dclipnodeBSP2_t, dclipnodesBSP2, MAX_MAP_CLIPNODES );
SetupArray( dedgeBSP2_t, dedgesBSP2, MAX_MAP_EDGES );
SetupArray( unsigned int, dmarksurfacesBSP2, MAX_MAP_MARKSURFACES );

qboolean isBSP2 = false;

// =============================================================================

/*
=============
SwapBSPFile

Byte swaps all data in a bsp file.
=============
*/
void
SwapBSPFile( qboolean todisk ) {
	int i, j, c;
	dmodel_t *d;
	dmiptexlump_t *mtl;

	// models
	for( i = 0; i < nummodels; i++ ) {
		d = &dmodels[ i ];

		for( j = 0; j < MAX_MAP_HULLS; j++ )
			d->headnode[ j ] = LittleLong( d->headnode[ j ] );

		d->visleafs = LittleLong( d->visleafs );
		d->firstface = LittleLong( d->firstface );
		d->numfaces = LittleLong( d->numfaces );

		for( j = 0; j < 3; j++ ) {
			d->mins[ j ] = LittleFloat( d->mins[ j ] );
			d->maxs[ j ] = LittleFloat( d->maxs[ j ] );
			d->origin[ j ] = LittleFloat( d->origin[ j ] );
		}
	}

	//
	// vertexes
	//
	for( i = 0; i < numvertexes; i++ ) {
		for( j = 0; j < 3; j++ )
			dvertexes[ i ].point[ j ] = LittleFloat( dvertexes[ i ].point[ j ] );
	}

	//
	// planes
	//
	for( i = 0; i < numplanes; i++ ) {
		for( j = 0; j < 3; j++ )
			dplanes[ i ].normal[ j ] = LittleFloat( dplanes[ i ].normal[ j ] );
		dplanes[ i ].dist = LittleFloat( dplanes[ i ].dist );
		dplanes[ i ].type = LittleLong( dplanes[ i ].type );
	}

	//
	// texinfos
	//
	for( i = 0; i < numtexinfo; i++ ) {
		for( j = 0; j < 8; j++ )
			texinfo[ i ].vecs[ 0 ][ j ] = LittleFloat( texinfo[ i ].vecs[ 0 ][ j ] );
		texinfo[ i ].miptex = LittleLong( texinfo[ i ].miptex );
		texinfo[ i ].flags = LittleLong( texinfo[ i ].flags );
	}

	//
	// faces
	//
	for( i = 0; i < numfaces; i++ ) {
		dfaces[ i ].texinfo = LittleShort( dfaces[ i ].texinfo );
		dfaces[ i ].planenum = LittleShort( dfaces[ i ].planenum );
		dfaces[ i ].side = LittleShort( dfaces[ i ].side );
		dfaces[ i ].lightofs = LittleLong( dfaces[ i ].lightofs );
		dfaces[ i ].firstedge = LittleLong( dfaces[ i ].firstedge );
		dfaces[ i ].numedges = LittleShort( dfaces[ i ].numedges );
	}

	//
	// nodes
	//
	for( i = 0; i < numnodes; i++ ) {
		dnodes[ i ].planenum = LittleLong( dnodes[ i ].planenum );
		for( j = 0; j < 3; j++ ) {
			dnodes[ i ].mins[ j ] = LittleShort( dnodes[ i ].mins[ j ] );
			dnodes[ i ].maxs[ j ] = LittleShort( dnodes[ i ].maxs[ j ] );
		}
		dnodes[ i ].children[ 0 ] = LittleShort( dnodes[ i ].children[ 0 ] );
		dnodes[ i ].children[ 1 ] = LittleShort( dnodes[ i ].children[ 1 ] );
		dnodes[ i ].firstface = LittleShort( dnodes[ i ].firstface );
		dnodes[ i ].numfaces = LittleShort( dnodes[ i ].numfaces );
	}

	//
	// leafs
	//
	for( i = 0; i < numleafs; i++ ) {
		dleafs[ i ].contents = LittleLong( dleafs[ i ].contents );
		for( j = 0; j < 3; j++ ) {
			dleafs[ i ].mins[ j ] = LittleShort( dleafs[ i ].mins[ j ] );
			dleafs[ i ].maxs[ j ] = LittleShort( dleafs[ i ].maxs[ j ] );
		}

		dleafs[ i ].firstmarksurface = LittleShort( dleafs[ i ].firstmarksurface );
		dleafs[ i ].nummarksurfaces = LittleShort( dleafs[ i ].nummarksurfaces );
		dleafs[ i ].visofs = LittleLong( dleafs[ i ].visofs );
	}

	//
	// clipnodes
	//
	for( i = 0; i < numclipnodes; i++ ) {
		dclipnodes[ i ].planenum = LittleLong( dclipnodes[ i ].planenum );
		dclipnodes[ i ].children[ 0 ] = LittleShort( dclipnodes[ i ].children[ 0 ] );
		dclipnodes[ i ].children[ 1 ] = LittleShort( dclipnodes[ i ].children[ 1 ] );
	}

	//
	// miptex
	//
	if( texdatasize ) {
		mtl = (dmiptexlump_t *) dtexdata;
		if( todisk )
			c = mtl->nummiptex;
		else
			c = LittleLong( mtl->nummiptex );
		mtl->nummiptex = LittleLong( mtl->nummiptex );
		for( i = 0; i < c; i++ )
			mtl->dataofs[ i ] = LittleLong( mtl->dataofs[ i ] );
	}

	//
	// marksurfaces
	//
	for( i = 0; i < nummarksurfaces; i++ )
		dmarksurfaces[ i ] = LittleShort( dmarksurfaces[ i ] );

	//
	// surfedges
	//
	for( i = 0; i < numsurfedges; i++ )
		dsurfedges[ i ] = LittleLong( dsurfedges[ i ] );

	//
	// edges
	//
	for( i = 0; i < numedges; i++ ) {
		dedges[ i ].v[ 0 ] = LittleShort( dedges[ i ].v[ 0 ] );
		dedges[ i ].v[ 1 ] = LittleShort( dedges[ i ].v[ 1 ] );
	}
}

void
SwapBSP2File( qboolean todisk ) {
	int i, j, c;
	dmodel_t *d;
	dmiptexlump_t *mtl;

	// models
	for( i = 0; i < nummodels; i++ ) {
		d = &dmodels[ i ];

		for( j = 0; j < MAX_MAP_HULLS; j++ )
			d->headnode[ j ] = LittleLong( d->headnode[ j ] );

		d->visleafs = LittleLong( d->visleafs );
		d->firstface = LittleLong( d->firstface );
		d->numfaces = LittleLong( d->numfaces );

		for( j = 0; j < 3; j++ ) {
			d->mins[ j ] = LittleFloat( d->mins[ j ] );
			d->maxs[ j ] = LittleFloat( d->maxs[ j ] );
			d->origin[ j ] = LittleFloat( d->origin[ j ] );
		}
	}

	//
	// vertexes
	//
	for( i = 0; i < numvertexes; i++ ) {
		for( j = 0; j < 3; j++ )
			dvertexes[ i ].point[ j ] = LittleFloat( dvertexes[ i ].point[ j ] );
	}

	//
	// planes
	//
	for( i = 0; i < numplanes; i++ ) {
		for( j = 0; j < 3; j++ )
			dplanes[ i ].normal[ j ] = LittleFloat( dplanes[ i ].normal[ j ] );
		dplanes[ i ].dist = LittleFloat( dplanes[ i ].dist );
		dplanes[ i ].type = LittleLong( dplanes[ i ].type );
	}

	//
	// texinfos
	//
	for( i = 0; i < numtexinfo; i++ ) {
		for( j = 0; j < 8; j++ )
			texinfo[ i ].vecs[ 0 ][ j ] = LittleFloat( texinfo[ i ].vecs[ 0 ][ j ] );
		texinfo[ i ].miptex = LittleLong( texinfo[ i ].miptex );
		texinfo[ i ].flags = LittleLong( texinfo[ i ].flags );
	}

	//
	// faces
	//
	for( i = 0; i < numfaces; i++ ) {
		dfacesBSP2[ i ].texinfo = LittleLong( dfacesBSP2[ i ].texinfo );
		dfacesBSP2[ i ].planenum = LittleLong( dfacesBSP2[ i ].planenum );

		dfacesBSP2[ i ].side = LittleShort( dfacesBSP2[ i ].side );
		//dfacesBSP2[ i ].side = LittleLong( dfacesBSP2[ i ].side );

		dfacesBSP2[ i ].lightofs = LittleLong( dfacesBSP2[ i ].lightofs );
		dfacesBSP2[ i ].firstedge = LittleLong( dfacesBSP2[ i ].firstedge );
		dfacesBSP2[ i ].numedges = LittleLong( dfacesBSP2[ i ].numedges );
	}

	//
	// nodes
	//
	for( i = 0; i < numnodes; i++ ) {
		dnodesBSP2[ i ].planenum = LittleLong( dnodesBSP2[ i ].planenum );
		for( j = 0; j < 3; j++ ) {
			//dnodesBSP2[ i ].mins[ j ] = LittleShort( dnodesBSP2[ i ].mins[ j ] );
			//dnodesBSP2[ i ].maxs[ j ] = LittleShort( dnodesBSP2[ i ].maxs[ j ] );
			dnodesBSP2[ i ].mins[ j ] = LittleFloat( dnodesBSP2[ i ].mins[ j ] );
			dnodesBSP2[ i ].maxs[ j ] = LittleFloat( dnodesBSP2[ i ].maxs[ j ] );
		}
		dnodesBSP2[ i ].children[ 0 ] = LittleLong( dnodesBSP2[ i ].children[ 0 ] );
		dnodesBSP2[ i ].children[ 1 ] = LittleLong( dnodesBSP2[ i ].children[ 1 ] );
		dnodesBSP2[ i ].firstface = LittleLong( dnodesBSP2[ i ].firstface );
		dnodesBSP2[ i ].numfaces = LittleLong( dnodesBSP2[ i ].numfaces );
	}

	//
	// leafs
	//
	for( i = 0; i < numleafs; i++ ) {
		dleafsBSP2[ i ].contents = LittleLong( dleafsBSP2[ i ].contents );
		for( j = 0; j < 3; j++ ) {
			//dleafsBSP2[ i ].mins[ j ] = LittleShort( dleafsBSP2[ i ].mins[ j ] );
			//dleafsBSP2[ i ].maxs[ j ] = LittleShort( dleafsBSP2[ i ].maxs[ j ] );
			dleafsBSP2[ i ].mins[ j ] = LittleFloat( dleafsBSP2[ i ].mins[ j ] );
			dleafsBSP2[ i ].maxs[ j ] = LittleFloat( dleafsBSP2[ i ].maxs[ j ] );
		}

		dleafsBSP2[ i ].firstmarksurface = LittleLong( dleafsBSP2[ i ].firstmarksurface );
		dleafsBSP2[ i ].nummarksurfaces = LittleLong( dleafsBSP2[ i ].nummarksurfaces );
		dleafsBSP2[ i ].visofs = LittleLong( dleafsBSP2[ i ].visofs );
	}

	//
	// clipnodes
	//
	for( i = 0; i < numclipnodes; i++ ) {
		dclipnodesBSP2[ i ].planenum = LittleLong( dclipnodesBSP2[ i ].planenum );
		dclipnodesBSP2[ i ].children[ 0 ] = LittleLong( dclipnodesBSP2[ i ].children[ 0 ] );
		dclipnodesBSP2[ i ].children[ 1 ] = LittleLong( dclipnodesBSP2[ i ].children[ 1 ] );
	}

	//
	// miptex
	//
	if( texdatasize ) {
		mtl = (dmiptexlump_t *) dtexdata;
		if( todisk )
			c = mtl->nummiptex;
		else
			c = LittleLong( mtl->nummiptex );
		mtl->nummiptex = LittleLong( mtl->nummiptex );
		for( i = 0; i < c; i++ )
			mtl->dataofs[ i ] = LittleLong( mtl->dataofs[ i ] );
	}

	//
	// marksurfaces
	//
	for( i = 0; i < nummarksurfaces; i++ )
		dmarksurfacesBSP2[ i ] = LittleLong( dmarksurfacesBSP2[ i ] );

	//
	// surfedges
	//
	for( i = 0; i < numsurfedges; i++ )
		dsurfedges[ i ] = LittleLong( dsurfedges[ i ] );

	//
	// edges
	//
	for( i = 0; i < numedges; i++ ) {
		dedgesBSP2[ i ].v[ 0 ] = LittleLong( dedgesBSP2[ i ].v[ 0 ] );
		dedgesBSP2[ i ].v[ 1 ] = LittleLong( dedgesBSP2[ i ].v[ 1 ] );
	}
}

dheader_t *header;

int
CopyLump( int lump, void *dest, int size ) {
	int length, ofs;

	length = header->lumps[ lump ].filelen;
	ofs = header->lumps[ lump ].fileofs;

	if( length % size )
		Message( MSGERR, "LoadBSPFile: Odd lump size" );

	NeedArrayBytes( dest, length );
	memcpy( dest, (byte *) header + ofs, length );

	return length / size;
}

/*
=============
LoadBSPFile
=============
*/
void
LoadBSPFile( char *filename ) {
	int i;

	//
	// load the file header
	//
	LoadFile( filename, (void **) &header );

	// swap the header
	for( i = 0; i < sizeof( dheader_t ) / 4; i++ )
		((int *) header )[ i ] = LittleLong(((int *) header )[ i ] );

	if( header->version != BSPVERSION && header->version != BSP2VERSION )
		Message( MSGERR, "%s is unsupported version %i", filename, header->version );

	nummodels = CopyLump( LUMP_MODELS, dmodels, sizeof( dmodel_t ));
	numvertexes = CopyLump( LUMP_VERTEXES, dvertexes, sizeof( dvertex_t ));
	numplanes = CopyLump( LUMP_PLANES, dplanes, sizeof( dplane_t ));
	numtexinfo = CopyLump( LUMP_TEXINFO, texinfo, sizeof( texinfo_t ));
	numsurfedges = CopyLump( LUMP_SURFEDGES, dsurfedges, sizeof( dsurfedges[ 0 ] ));

	if( header->version == BSP2VERSION ) {
		Message( MSGALWAYS, "Loaded File Format : BSP2");
		isBSP2 = true;

		numleafs = CopyLump( LUMP_LEAFS, dleafsBSP2, sizeof( dleafBSP2_t ));
		numnodes = CopyLump( LUMP_NODES, dnodesBSP2, sizeof( dnodeBSP2_t ));
		numclipnodes = CopyLump( LUMP_CLIPNODES, dclipnodesBSP2, sizeof( dclipnodeBSP2_t ));
		numfaces = CopyLump( LUMP_FACES, dfacesBSP2, sizeof( dfaceBSP2_t ));
		nummarksurfaces = CopyLump( LUMP_MARKSURFACES, dmarksurfacesBSP2, sizeof( dmarksurfacesBSP2[ 0 ] ));
		numedges = CopyLump( LUMP_EDGES, dedgesBSP2, sizeof( dedgeBSP2_t ));
	} else {
		Message( MSGALWAYS, "Loaded File Format : BSP29");

		// set up some required arrays
		CreateArray( dleaf_t, dleafs, MAX_MAP_LEAFS );
		CreateArray( dnode_t, dnodes, MAX_MAP_NODES );
		CreateArray( dface_t, dfaces, MAX_MAP_FACES );
		CreateArray( dclipnode_t, dclipnodes, MAX_MAP_CLIPNODES );
		CreateArray( dedge_t, dedges, MAX_MAP_EDGES );
		CreateArray( unsigned short, dmarksurfaces, MAX_MAP_MARKSURFACES );

		numleafs = CopyLump( LUMP_LEAFS, dleafs, sizeof( dleaf_t ));
		numnodes = CopyLump( LUMP_NODES, dnodes, sizeof( dnode_t ));
		numclipnodes = CopyLump( LUMP_CLIPNODES, dclipnodes, sizeof( dclipnode_t ));
		numfaces = CopyLump( LUMP_FACES, dfaces, sizeof( dface_t ));
		nummarksurfaces = CopyLump( LUMP_MARKSURFACES, dmarksurfaces, sizeof( dmarksurfaces[ 0 ] ));
		numedges = CopyLump( LUMP_EDGES, dedges, sizeof( dedge_t ));
	}

	texdatasize = CopyLump( LUMP_TEXTURES, dtexdata, 1 );
	visdatasize = CopyLump( LUMP_VISIBILITY, dvisdata, 1 );
	lightdatasize = CopyLump( LUMP_LIGHTING, dlightdata, 1 );
	entdatasize = CopyLump( LUMP_ENTITIES, dentdata, 1 );

	//
	// swap everything
	//
	if( isBSP2 ) {
		SwapBSP2File( false );
	} else {
		SwapBSPFile( false );
	}

	FreeOther( header ); // everything has been copied out
}

// ============================================================================

FILE *wadfile;
dheader_t	outheader;
char		*wadfilename;

void
AddLump( int lumpnum, void *data, int len ) {
	lump_t	*lump;
	int		extra;
	byte	padd[ 4 ] = { 0, 0, 0, 0 };

	lump = &header->lumps[ lumpnum ];

	lump->fileofs = LittleLong( ftell( wadfile ));
	lump->filelen = LittleLong( len );
	SafeWrite( wadfile, wadfilename, data, len );

	extra = (( len + 3 ) & ~3 ) - len;

	if( extra > 0 )
		SafeWrite( wadfile, wadfilename, padd, extra ); // Padd with zeroes to even 4-byte boundary
}

#if 1 // Rotating brush support ... err ... actually just an MH enhancement
int skipcount;

void
SkipBSPFile( void ) {
	int i, j, k;

	unsigned short *leafmarks;
	dleaf_t		*leaf;
	dface_t		*face;
	texinfo_t	*ti;
	miptex_t	*textures, *texture;
	char		*name;
	dmiptexlump_t	*miptexlump;
	dmodel_t	*model;
	dface_t		*modfaces;

	miptexlump = (dmiptexlump_t *) dtexdata;
	textures = (miptex_t *) dtexdata;
	skipcount = 0;

	for( i = 0, leaf = dleafs; i < numleafs; i++, leaf++ ) {
		leafmarks = dmarksurfaces + leaf->firstmarksurface;

		for( j = 0; j < leaf->nummarksurfaces; ) {
			face = dfaces + leafmarks[ j ];
			ti = texinfo + face->texinfo;

			texture = (miptex_t *) ((byte *) textures + miptexlump->dataofs[ ti->miptex ] );
			name = texture->name;

			if(
				!strcmp( name, "skip" )
				|| !strcmp( name, "*waterskip" )
				|| !strcmp( name, "*slimeskip" )
				|| !strcmp( name, "*lavaskip" )
			) {
				// copy each remaining marksurface to previous slot
				for( k = j; k < leaf->nummarksurfaces - 1; k++ )
					leafmarks[ k ] = leafmarks[ k + 1 ];

				// reduce marksurface count by one
				leaf->nummarksurfaces--;

				skipcount++;
			} else {
				j++;
			}
		}
	}

	// loop through all the models, editing surface lists (this takes care of brush entities)
	for( i = 0, model = dmodels; i < nummodels; i++, model++ ) {
		if( i == 0 ) continue; // model 0 is worldmodel

		modfaces = dfaces + model->firstface;

		for( j = 0; j < model->numfaces; ) {
			face = modfaces + j;
			ti = texinfo + face->texinfo;
			texture = (miptex_t *) ((byte *) textures + miptexlump->dataofs[ ti->miptex ] );
			name = texture->name;

			if(
				!strcmp( name, "skip" )
				|| !strcmp( name, "*waterskip" )
				|| !strcmp( name, "*slimeskip" )
				|| !strcmp( name, "*lavaskip" )
			) {
				// copy each remaining face to previous slot
				for( k = j; k < model->numfaces - 1; k++ )
					modfaces[ k ] = modfaces[ k + 1 ];

				// reduce face count by one
				model->numfaces--;

				skipcount++;
			} else
				j++;
		}
	}

	Message( MSGALWAYS, "\nSkipped %i surfaces", skipcount );
}

void
SkipBSP2File( void ) {
	int i, j, k;

	// unsigned int *leafmarks;
	// Baker: ....
	unsigned int	*leafmarks;
	dleafBSP2_t		*leaf;
	dfaceBSP2_t		*face;
	dfaceBSP2_t		*modfaces;

	texinfo_t		*ti;
	miptex_t		*textures, *texture;
	char			*name;
	dmiptexlump_t	*miptexlump;
	dmodel_t		*model;

	miptexlump = (dmiptexlump_t *) dtexdata;
	textures = (miptex_t *) dtexdata;
	skipcount = 0;

	for( i = 0, leaf = dleafsBSP2; i < numleafs; i++, leaf++ ) {
		leafmarks = dmarksurfacesBSP2 + leaf->firstmarksurface;

		for( j = 0; j < leaf->nummarksurfaces; ) {
			face = dfacesBSP2 + leafmarks[ j ];
			ti = texinfo + face->texinfo;

			texture = (miptex_t *) ((byte *) textures + miptexlump->dataofs[ ti->miptex ] );
			name = texture->name;

			if(
				!strcmp( name, "skip" )
				|| !strcmp( name, "*waterskip" )
				|| !strcmp( name, "*slimeskip" )
				|| !strcmp( name, "*lavaskip" )
			) {
				// copy each remaining marksurface to previous slot
				for( k = j; k < leaf->nummarksurfaces - 1; k++ )
					leafmarks[ k ] = leafmarks[ k + 1 ];

				// reduce marksurface count by one
				leaf->nummarksurfaces--;

				skipcount++;
			} else {
				j++;
			}
		}
	}

	// loop through all the models, editing surface lists (this takes care of brush entities)
	for( i = 0, model = dmodels; i < nummodels; i++, model++ ) {
		if( i == 0 ) continue; // model 0 is worldmodel

		modfaces = dfacesBSP2 + model->firstface;

		for( j = 0; j < model->numfaces; ) {
			face = modfaces + j;
			ti = texinfo + face->texinfo;
			texture = (miptex_t *) ((byte *) textures + miptexlump->dataofs[ ti->miptex ] );
			name = texture->name;

			if(
				!strcmp( name, "skip" )
				|| !strcmp( name, "*waterskip" )
				|| !strcmp( name, "*slimeskip" )
				|| !strcmp( name, "*lavaskip" )
			) {
				// copy each remaining face to previous slot
				for( k = j; k < model->numfaces - 1; k++ )
					modfaces[ k ] = modfaces[ k + 1 ];

				// reduce face count by one
				model->numfaces--;

				skipcount++;
			} else
				j++;
		}
	}

	Message( MSGALWAYS, "\nSkipped %i surfaces", skipcount );
}

#endif

/*
=============
WriteBSPFile

Swaps the bsp file in place, so it should not be referenced again
=============
*/

// ugly :/
void
MakeLowLimitData() {
	int i;

	// Leafs
	dleafs = malloc( numleafs * sizeof( dleaf_t ));

	for( i = 0; i < numleafs; ++i ) {
		dleaf_t	*a		= &dleafs[i];
		dleafBSP2_t *b	= &dleafsBSP2[i];

		a->ambient_level[0]		= b->ambient_level[0];
		a->ambient_level[1]		= b->ambient_level[1];
		a->ambient_level[2]		= b->ambient_level[2];
		a->ambient_level[3]		= b->ambient_level[3];
		a->contents				= b->contents;
		a->firstmarksurface		= (unsigned short) b->firstmarksurface;
		a->maxs[0]				= (short) b->maxs[0];
		a->maxs[1]				= (short) b->maxs[1];
		a->maxs[2]				= (short) b->maxs[2];
		a->mins[0]				= (short) b->mins[0];
		a->mins[1]				= (short) b->mins[1];
		a->mins[2]				= (short) b->mins[2];
		a->nummarksurfaces		= (unsigned short) b->nummarksurfaces;
		a->visofs				= b->visofs;
	}

	// Nodes
	dnodes = malloc( numnodes * sizeof( dnode_t ));

	for( i = 0; i < numnodes; ++i ) {
		dnode_t	*a		= &dnodes[i];
		dnodeBSP2_t *b	= &dnodesBSP2[i];

		a->children[0]	= (short) b->children[0];
		a->children[1]	= (short) b->children[1];
		a->firstface	= (unsigned short) b->firstface;
		a->maxs[0]		= (short) b->maxs[0];
		a->maxs[1]		= (short) b->maxs[1];
		a->maxs[2]		= (short) b->maxs[2];
		a->mins[0]		= (short) b->mins[0];
		a->mins[1]		= (short) b->mins[1];
		a->mins[2]		= (short) b->mins[2];
		a->numfaces		= (unsigned short) b->numfaces;
		a->planenum		= b->planenum;
	}

	// Faces
	dfaces = malloc( numfaces * sizeof( dface_t ));

	for( i = 0; i < numfaces; ++i ) {
		dface_t *a		= &dfaces[i];
		dfaceBSP2_t *b	= &dfacesBSP2[i];

		a->firstedge	= b->firstedge;
		a->lightofs		= b->lightofs;
		a->numedges		= (short) b->numedges;
		a->planenum		= (short) b->planenum;
		a->side			= (short) b->side;
		a->styles[0]	= b->styles[0];
		a->styles[1]	= b->styles[1];
		a->styles[2]	= b->styles[2];
		a->styles[3]	= b->styles[3];
		a->texinfo		= (short) b->texinfo;
	}

	// ClipNodes
	dclipnodes = malloc( numclipnodes * sizeof( dclipnode_t ));

	for( i = 0; i < numclipnodes; ++i ) {
		dclipnode_t *a		= &dclipnodes[i];
		dclipnodeBSP2_t *b	= &dclipnodesBSP2[i];

		a->children[0]	= (short) b->children[0];
		a->children[1]	= (short) b->children[1];
		a->planenum		= b->planenum;
	}

	// MarkSurfaces
	dmarksurfaces = malloc( nummarksurfaces * sizeof( unsigned short ));

	for( i = 0; i < nummarksurfaces; ++i ) {
		unsigned short *a	= &dmarksurfaces[i];
		unsigned int *b		= &dmarksurfacesBSP2[i];

		*a = (unsigned short) *b;
	}

	// Edges
	dedges = malloc( numedges * sizeof( dedge_t ));

	for( i = 0; i < numedges; ++i ) {
		dedge_t *a		= &dedges[i];
		dedgeBSP2_t *b	= &dedgesBSP2[i];

		a->v[0]	= (unsigned short) b->v[0];
		a->v[1]	= (unsigned short) b->v[1];
	}
}

void
ClearLowLimitData() {
	free( dedges );
	free( dmarksurfaces );
	free( dclipnodes );
	free( dfaces );
	free( dnodes );
	free( dleafs );
}

qboolean
IsBSP2Needed() {
	qboolean
		dataNeedsBSP2 = (
			//( nummodels				> 8192 ) ||
			( nummarksurfaces		> 65535 ) || // *
			( numfaces				> 65535 ) || // *
			( numvertexes			> 65535 ) || // * due to unsigned short indexing used in edges
			( numtexinfo			> 32767 ) || // * due to face->texinfo
			( numclipnodes			> 65500 ) ||
			( numplanes				> 32767 ) || // * due to face->planenum
			( numleafs				> 32767 ) || // *
			( numnodes				> 32767 ) || // *
			( dmodels[0].mins[0]	< -32767.0f ) ||
			( dmodels[0].mins[1]	< -32767.0f ) ||
			( dmodels[0].mins[2]	< -32767.0f ) ||
			( dmodels[0].maxs[0]	> 32767.0f ) ||
			( dmodels[0].maxs[1]	> 32767.0f ) ||
			( dmodels[0].maxs[2]	> 32767.0f )
		);

	return( isBSP2 || dataNeedsBSP2 || options.useBSP2 );
}

void
WriteBSPFile( char *filename ) {
	qboolean
		writeBSP2 = IsBSP2Needed();

	header = &outheader;
	memset( header, 0, sizeof( dheader_t ));

	if( !writeBSP2 && !options.onlyents ) {
		MakeLowLimitData();
	}

	if( writeBSP2 ) {
		if( !options.noskip )
			SkipBSP2File();

		SwapBSP2File( true );
		header->version = LittleLong( BSP2VERSION );
	} else {
		if( !options.noskip )
			SkipBSPFile();

		SwapBSPFile( true );
		header->version = LittleLong( BSPVERSION );
	}

	wadfile = SafeOpen( filename, "wb", true, &wadfilename );
	SafeWrite( wadfile, wadfilename, header, sizeof( dheader_t )); // overwritten later

	AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ));
	AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertex_t ));
	AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo_t ));
	AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[ 0 ] ));
	AddLump( LUMP_MODELS, dmodels, nummodels * sizeof( dmodel_t ));
	AddLump( LUMP_LIGHTING, dlightdata, lightdatasize );
	AddLump( LUMP_VISIBILITY, dvisdata, visdatasize );
	AddLump( LUMP_ENTITIES, dentdata, entdatasize );
	AddLump( LUMP_TEXTURES, dtexdata, texdatasize );

	if( writeBSP2 ) {
		Message( MSGALWAYS, "Writing File Format : BSP2");

		AddLump( LUMP_LEAFS, dleafsBSP2, numleafs * sizeof( dleafBSP2_t ));
		AddLump( LUMP_NODES, dnodesBSP2, numnodes * sizeof( dnodeBSP2_t ));
		AddLump( LUMP_FACES, dfacesBSP2, numfaces * sizeof( dfaceBSP2_t ));
		AddLump( LUMP_CLIPNODES, dclipnodesBSP2, numclipnodes * sizeof( dclipnodeBSP2_t ));
		AddLump( LUMP_MARKSURFACES, dmarksurfacesBSP2, nummarksurfaces * sizeof( dmarksurfacesBSP2[ 0 ] ));
		AddLump( LUMP_EDGES, dedgesBSP2, numedges * sizeof( dedgeBSP2_t ));
	} else {
		Message( MSGALWAYS, "Writing File Format : BSP29");

		AddLump( LUMP_LEAFS, dleafs, numleafs * sizeof( dleaf_t ));
		AddLump( LUMP_NODES, dnodes, numnodes * sizeof( dnode_t ));
		AddLump( LUMP_FACES, dfaces, numfaces * sizeof( dface_t ));
		AddLump( LUMP_CLIPNODES, dclipnodes, numclipnodes * sizeof( dclipnode_t ));
		AddLump( LUMP_MARKSURFACES, dmarksurfaces, nummarksurfaces * sizeof( dmarksurfaces[ 0 ] ));
		AddLump( LUMP_EDGES, dedges, numedges * sizeof( dedge_t ));
	}

	if( !writeBSP2 && !options.onlyents ) {
		ClearLowLimitData();
	}

	SafeSeek( wadfile, wadfilename, 0 );
	SafeWrite( wadfile, wadfilename, header, sizeof( dheader_t ));
	SafeClose( wadfile, wadfilename );
}

// ============================================================================

/*
=============
PrintBSPFileSizes

Dumps info about current file
=============
*/
void
PrintBSPFileSizes( void ) {
	Message( MSGNOVERBOSE, "%6i planes      %7i", numplanes,		(int) ( numplanes * sizeof( dplane_t )));
	Message( MSGNOVERBOSE, "%6i vertexes    %7i", numvertexes,		(int) ( numvertexes * sizeof( dvertex_t )));
	Message( MSGNOVERBOSE, "%6i nodes       %7i", numnodes,			(int) ( numnodes * sizeof( dnode_t )));
	Message( MSGNOVERBOSE, "%6i texinfo     %7i", numtexinfo,		(int) ( numtexinfo * sizeof( texinfo_t )));
	Message( MSGNOVERBOSE, "%6i faces       %7i", numfaces,			(int) ( numfaces * sizeof( dface_t )));
	Message( MSGNOVERBOSE, "%6i clipnodes   %7i", numclipnodes,		(int) ( numclipnodes * sizeof( dclipnode_t )));
	Message( MSGNOVERBOSE, "%6i leafs       %7i", numleafs,			(int) ( numleafs * sizeof( dleaf_t )));
	Message( MSGNOVERBOSE, "%6i marksurfaces%7i", nummarksurfaces,	(int) ( nummarksurfaces * sizeof( unsigned short )));
	Message( MSGNOVERBOSE, "%6i surfedges   %7i", numsurfedges,		(int) ( numsurfedges * sizeof( int )));
	Message( MSGNOVERBOSE, "%6i edges       %7i", numedges,			(int) ( numedges * sizeof( dedge_t )));

	Message( MSGNOVERBOSE, "%6i textures  %9i", texdatasize ? ((dmiptexlump_t *) dtexdata )->nummiptex : 0, texdatasize );
	Message( MSGNOVERBOSE, "%6s lightdata %9i", "", lightdatasize );
	Message( MSGNOVERBOSE, "%6s visdata   %9i", "", visdatasize );
	Message( MSGNOVERBOSE, "%6s entdata   %9i", "", entdatasize );
}

void
PrintBSP2FileSizes( void ) {
	Message( MSGNOVERBOSE, "%6i planes      %7i", numplanes,		(int)( numplanes * sizeof( dplane_t )));
	Message( MSGNOVERBOSE, "%6i vertexes    %7i", numvertexes,		(int)( numvertexes * sizeof( dvertex_t )));
	Message( MSGNOVERBOSE, "%6i nodes       %7i", numnodes,			(int)( numnodes * sizeof( dnodeBSP2_t )));
	Message( MSGNOVERBOSE, "%6i texinfo     %7i", numtexinfo,		(int)( numtexinfo * sizeof( texinfo_t )));
	Message( MSGNOVERBOSE, "%6i faces       %7i", numfaces,			(int)( numfaces * sizeof( dfaceBSP2_t )));
	Message( MSGNOVERBOSE, "%6i clipnodes   %7i", numclipnodes,		(int)( numclipnodes * sizeof( dclipnodeBSP2_t )));
	Message( MSGNOVERBOSE, "%6i leafs       %7i", numleafs,			(int)( numleafs * sizeof( dleafBSP2_t )));
	Message( MSGNOVERBOSE, "%6i marksurfaces%7i", nummarksurfaces,	(int)( nummarksurfaces * sizeof( unsigned int )));
	Message( MSGNOVERBOSE, "%6i surfedges   %7i", numsurfedges,		(int)( numsurfedges * sizeof( int )));
	Message( MSGNOVERBOSE, "%6i edges       %7i", numedges,			(int)( numedges * sizeof( dedgeBSP2_t )));

	Message( MSGNOVERBOSE, "%6i textures  %9i",	texdatasize ? ((dmiptexlump_t *) dtexdata )->nummiptex : 0, texdatasize );
	Message( MSGNOVERBOSE, "%6s lightdata %9i", "", lightdatasize );
	Message( MSGNOVERBOSE, "%6s visdata   %9i", "", visdatasize );
	Message( MSGNOVERBOSE, "%6s entdata   %9i", "", entdatasize );
}