
#include "cmdlib.h"
#include "mathlib.h"
#include "bspfile.h"

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

int nummodels;
dmodel_t *dmodels;

int	visdatasize;
byte	*dvisdata;

int	lightdatasize;
byte	*dlightdata;

int	texdatasize;
byte	*dtexdata;         // (dmiptexlump_t)

int	entdatasize;
char	*dentdata;

int numleafs;
dleaf_t *dleafs;

int numplanes;
dplane_t *dplanes;

int numvertexes;
dvertex_t *dvertexes;

int numnodes;
dnode_t *dnodes;

int numtexinfo;
texinfo_t *texinfo;

int numfaces;
dface_t *dfaces;

int numclipnodes;
dclipnode_t *dclipnodes;

int numedges;
dedge_t *dedges;

int nummarksurfaces;
unsigned short *dmarksurfaces;

int	numsurfedges;
int	*dsurfedges;

#ifdef FEATURE_BSP2
dleafBSP2_t *dleafsBSP2;
dnodeBSP2_t *dnodesBSP2;
dfaceBSP2_t *dfacesBSP2;
dclipnodeBSP2_t *dclipnodesBSP2;
dedgeBSP2_t *dedgesBSP2;
unsigned int *dmarksurfacesBSP2;

qboolean isBSP2 = false;
#endif

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

static unsigned RecursLevel;

/*
=============
ChkNode

Validates bsp node tree
=============
*/
static void
ChkNode( int nodenum ) {
	dnode_t *dn;
	int	i;

	if( ++RecursLevel > 4096 ) // 512 seems enough for huge maps and 8192 might create stack overflow in the engine
		Error( "ChkNode: excessive tree depth (max = %d)", 4096 );

	if( nodenum < 0 ) {
		int leaf = -nodenum - 1;

		if( leaf >= numleafs )
			Error( "ChkNode: leaf out of bounds (%d, max = %d)", leaf, numleafs );

		--RecursLevel;
		return;
	}

	if( nodenum >= numnodes )
		Error( "ChkNode: node out of bounds (%d, max = %d)", nodenum, numnodes );

	dn = dnodes + nodenum;

	for( i = 0; i < 2; ++i )
		ChkNode( dn->children[ i ] );

	--RecursLevel;
}

#ifdef FEATURE_BSP2
static void
ChkNodeBSP2( int nodenum ) {
	dnodeBSP2_t *dn;
	int	i;

	if( ++RecursLevel > 4096 ) // 512 seems enough for huge maps and 8192 might create stack overflow in the engine
		Error( "ChkNode: excessive tree depth (max = %d)", 4096 );

	if( nodenum < 0 ) {
		int leaf = -nodenum - 1;

		if( leaf >= numleafs )
			Error( "ChkNode: leaf out of bounds (%d, max = %d)", leaf, numleafs );

		--RecursLevel;
		return;
	}

	if( nodenum >= numnodes )
		Error( "ChkNode: node out of bounds (%d, max = %d)", nodenum, numnodes );

	dn = dnodesBSP2 + nodenum;

	for( i = 0; i < 2; ++i )
		ChkNodeBSP2( dn->children[ i ] );

	--RecursLevel;
}
#endif

/*
=============
ChkBSPFile

Validates data in bsp
=============
*/
void
ChkBSPFile( void ) {
	int i, j;

	//
	// faces
	//
	for( i = 0; i < numfaces; i++ ) {
		if( dfaces[ i ].texinfo < 0 || dfaces[ i ].texinfo >= numtexinfo )
			Error( "ChkBSPFile: texinfo out of bounds (%d, max = %d)", dfaces[ i ].texinfo, numtexinfo );

		if( dfaces[ i ].planenum < 0 || dfaces[ i ].planenum >= numplanes )
			Error( "ChkBSPFile: face->planenum out of bounds (%d, max = %d)", dfaces[ i ].planenum, numplanes );

		if( dfaces[ i ].firstedge < 0 || dfaces[ i ].numedges < 0 || dfaces[ i ].firstedge + dfaces[ i ].numedges > numsurfedges )
			Error( "ChkBSPFile: firstedge/numedges out of bounds (%d/%d, max = %d)", dfaces[ i ].firstedge, dfaces[ i ].numedges, numsurfedges );
	}

	//
	// nodes
	//
	for( i = 0; i < numnodes; i++ ) {
		if( dnodes[ i ].planenum < 0 || dnodes[ i ].planenum >= numplanes )
			Error( "ChkBSPFile: node->planenum out of bounds (%d, max = %d)", dnodes[ i ].planenum, numplanes );

		if( dnodes[ i ].firstface + dnodes[ i ].numfaces > numfaces )
			Error( "ChkBSPFile: firstface/numfaces out of bounds (%d/%d, max = %d)", dnodes[ i ].firstface, dnodes[ i ].numfaces, numfaces );
	}

	RecursLevel = 0;

	ChkNode( 0 );

	//
	// miptex
	//
	if( texdatasize ) {
		dmiptexlump_t *mtl = (dmiptexlump_t *) dtexdata;
		int c = mtl->nummiptex;

		if((byte *) &mtl->dataofs[ c ] - (byte *) mtl > (unsigned) texdatasize )
			Error( "ChkBSPFile: texture index %d is outside texdata size %d", c, texdatasize );

		for( i = 0; i < c; i++ ) {
			int offset = mtl->dataofs[ i ];

			if( offset != -1 && offset >= (unsigned) texdatasize )
				Error( "ChkBSPFile: texture offset %u is outside texdata size %d", offset, texdatasize );
		}
	}

	//
	// surfedges
	//
	for( i = 0; i < numsurfedges; i++ ) {
		int aedge = abs( dsurfedges[ i ] );

		if( aedge >= numedges )
			Error( "ChkBSPFile: edge out of bounds (%d, max = %d)", aedge, numedges );
	}

	//
	// edges
	//
	for( i = 0; i < numedges; i++ ) {
		for( j = 0; j < 2; j++ ) {
			if( i == 0 && j == 0 )
				continue; // Why is sometimes this edge invalid?

			if( dedges[ i ].v[ j ] >= numvertexes )
				Error( "ChkBSPFile: vertex out of bounds (%d, max = %d)", dedges[ i ].v[ j ], numvertexes );
		}
	}
}

#ifdef FEATURE_BSP2
void
ChkBSP2File( void ) {
	int i, j;

	//
	// faces
	//
	for( i = 0; i < numfaces; i++ ) {
		if( dfacesBSP2[ i ].texinfo < 0 || dfacesBSP2[ i ].texinfo >= numtexinfo )
			Error( "ChkBSPFile: texinfo out of bounds (%d, max = %d)", dfacesBSP2[ i ].texinfo, numtexinfo );

		if( dfacesBSP2[ i ].planenum < 0 || dfacesBSP2[ i ].planenum >= numplanes )
			Error( "ChkBSPFile: face->planenum out of bounds (%d, max = %d)", dfacesBSP2[ i ].planenum, numplanes );

		if( dfacesBSP2[ i ].firstedge < 0 || dfacesBSP2[ i ].numedges < 0 || dfacesBSP2[ i ].firstedge + dfacesBSP2[ i ].numedges > numsurfedges )
			Error( "ChkBSPFile: firstedge/numedges out of bounds (%d/%d, max = %d)", dfacesBSP2[ i ].firstedge, dfacesBSP2[ i ].numedges, numsurfedges );
	}

	//
	// nodes
	//
	for( i = 0; i < numnodes; i++ ) {
		if( dnodesBSP2[ i ].planenum < 0 || dnodesBSP2[ i ].planenum >= numplanes )
			Error( "ChkBSPFile: node->planenum out of bounds (%d, max = %d)", dnodesBSP2[ i ].planenum, numplanes );

		if( dnodesBSP2[ i ].firstface + dnodesBSP2[ i ].numfaces > numfaces )
			Error( "ChkBSPFile: firstface/numfaces out of bounds (%d/%d, max = %d)", dnodesBSP2[ i ].firstface, dnodesBSP2[ i ].numfaces, numfaces );
	}

	RecursLevel = 0;

	ChkNodeBSP2( 0 );

	//
	// miptex
	//
	if( texdatasize ) {
		dmiptexlump_t *mtl = (dmiptexlump_t *) dtexdata;
		int c = mtl->nummiptex;

		if((byte *) &mtl->dataofs[ c ] - (byte *) mtl > (unsigned) texdatasize )
			Error( "ChkBSPFile: texture index %d is outside texdata size %d", c, texdatasize );

		for( i = 0; i < c; i++ ) {
			int offset = mtl->dataofs[ i ];

			if( offset != -1 && offset >= (unsigned) texdatasize )
				Error( "ChkBSPFile: texture offset %u is outside texdata size %d", offset, texdatasize );
		}
	}

	//
	// surfedges
	//
	for( i = 0; i < numsurfedges; i++ ) {
		int aedge = abs( dsurfedges[ i ] );

		if( aedge >= numedges )
			Error( "ChkBSPFile: edge out of bounds (%d, max = %d)", aedge, numedges );
	}

	//
	// edges
	//
	for( i = 0; i < numedges; i++ ) {
		for( j = 0; j < 2; j++ ) {
			if( i == 0 && j == 0 )
				continue; // Why is sometimes this edge invalid?

			if( dedgesBSP2[ i ].v[ j ] >= numvertexes )
				Error( "ChkBSPFile: vertex out of bounds (%d, max = %d)", dedgesBSP2[ i ].v[ j ], numvertexes );
		}
	}
}
#endif

/*
=============
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 ] );
	}
}

#ifdef FEATURE_BSP2
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 ] );
	}
}
#endif

dheader_t *header;
int FSize;

int
CopyLump( int lump, void **dest, int size, char object[] ) {
	int length, ofs;

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

	if( length % size )
		Error( "LoadBSPFile: odd %s lump size", object );

	if( length > 0 && ofs + length > FSize )
		Error( "LoadBSPFile: %s offset+length %d is outside file size %d", object, ofs + length, FSize );

	if( *dest != NULL )
		free( *dest );

	*dest = malloc( length + 1 ); // One extra byte just in case ...

	memcpy( *dest, (byte *) header + ofs, length );
	*((byte *) *dest + length ) = 0; // Entity lump seems to require this

	return length / size;
}

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

	//
	// load the file header
	//
	if( header ) {
		// After error, we might come back here; avoid leaks
		free( header );
		header = NULL;
	}

	FSize = LoadFile( filename, (void **) &header );

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

#ifdef FEATURE_BSP2
	if( header->version != BSPVERSION && header->version != BSP2VERSION )
		Error( "%s is unsupported version %i", filename, header->version );
#else
	if( header->version != BSPVERSION )
		Error( "%s is unsupported version %i", filename, header->version );
#endif

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

#ifdef FEATURE_BSP2
	if( header->version == BSP2VERSION ) {
		logprintf("File is BSP2 Format\n");
		isBSP2 = true;

		numleafs = CopyLump( LUMP_LEAFS, &dleafsBSP2, sizeof( dleafBSP2_t ), "Leaf" );
		numnodes = CopyLump( LUMP_NODES, &dnodesBSP2, sizeof( dnodeBSP2_t ), "Node" );
		numclipnodes = CopyLump( LUMP_CLIPNODES, &dclipnodesBSP2, sizeof( dclipnodeBSP2_t ), "Clipnode" );
		numfaces = CopyLump( LUMP_FACES, &dfacesBSP2, sizeof( dfaceBSP2_t ), "Face" );
		nummarksurfaces = CopyLump( LUMP_MARKSURFACES, &dmarksurfacesBSP2, sizeof( dmarksurfacesBSP2[ 0 ] ), "Marksurface" );
		numedges = CopyLump( LUMP_EDGES, &dedgesBSP2, sizeof( dedgeBSP2_t ), "Edge" );
	} else {
		numleafs = CopyLump( LUMP_LEAFS, &dleafs, sizeof( dleaf_t ), "Leaf" );
		numnodes = CopyLump( LUMP_NODES, &dnodes, sizeof( dnode_t ), "Node" );
		numclipnodes = CopyLump( LUMP_CLIPNODES, &dclipnodes, sizeof( dclipnode_t ), "Clipnode" );
		numfaces = CopyLump( LUMP_FACES, &dfaces, sizeof( dface_t ), "Face" );
		nummarksurfaces = CopyLump( LUMP_MARKSURFACES, &dmarksurfaces, sizeof( dmarksurfaces[ 0 ] ), "Marksurface" );
		numedges = CopyLump( LUMP_EDGES, &dedges, sizeof( dedge_t ), "Edge" );
	}
#else
	numleafs = CopyLump( LUMP_LEAFS, &dleafs, sizeof( dleaf_t ), "Leaf" );
	numnodes = CopyLump( LUMP_NODES, &dnodes, sizeof( dnode_t ), "Node" );
	numclipnodes = CopyLump( LUMP_CLIPNODES, &dclipnodes, sizeof( dclipnode_t ), "Clipnode" );
	numfaces = CopyLump( LUMP_FACES, &dfaces, sizeof( dface_t ), "Face" );
	nummarksurfaces = CopyLump( LUMP_MARKSURFACES, &dmarksurfaces, sizeof( dmarksurfaces[ 0 ] ), "Marksurface" );
	numedges = CopyLump( LUMP_EDGES, &dedges, sizeof( dedge_t ), "Edge" );
#endif

	texdatasize = CopyLump( LUMP_TEXTURES, &dtexdata, 1, "Texture" );
	visdatasize = CopyLump( LUMP_VISIBILITY, &dvisdata, 1, "Visdata" );
	lightdatasize = CopyLump( LUMP_LIGHTING, &dlightdata, 1, "Lightdata" );
	entdatasize = CopyLump( LUMP_ENTITIES, &dentdata, 1, "Entdata" );

	//
	// swap everything
	//
#ifdef FEATURE_BSP2
	if( header->version == BSP2VERSION ) {
		SwapBSP2File( false );
		ChkBSP2File();
	} else {
		SwapBSPFile( false );
		ChkBSPFile();
	}
#else
	SwapBSPFile( false );
	ChkBSPFile();
#endif

	free( header ); // everything has been copied out
	header = NULL;
}

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

FILE *wadfile;
dheader_t outheader;

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, data, len );

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

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

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

Swaps the bsp file in place, so it should not be referenced again
=============
*/
void
WriteBSPFile( char *filename ) {
	header = &outheader;
	memset( header, 0, sizeof( dheader_t ));

	SwapBSPFile( true );

	header->version = LittleLong( BSPVERSION );

	wadfile = SafeOpenWrite( filename );
	SafeWrite( wadfile, header, sizeof( dheader_t )); // overwritten later

	AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ));
	AddLump( LUMP_LEAFS, dleafs, numleafs * sizeof( dleaf_t ));
	AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertex_t ));
	AddLump( LUMP_NODES, dnodes, numnodes * sizeof( dnode_t ));
	AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo_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_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[ 0 ] ));
	AddLump( LUMP_EDGES, dedges, numedges * sizeof( dedge_t ));
	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 );

	fseek( wadfile, 0, SEEK_SET );
	SafeWrite( wadfile, header, sizeof( dheader_t ));
	fclose( wadfile );
}

#ifdef FEATURE_BSP2
void
WriteBSP2File( char *filename ) {
	logprintf( "\nWriting BSP2 Format" );

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

	SwapBSP2File( true );

	header->version = LittleLong( BSP2VERSION );

	wadfile = SafeOpenWrite( filename );
	SafeWrite( wadfile, header, sizeof( dheader_t )); // overwritten later

	AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ));
	AddLump( LUMP_LEAFS, dleafsBSP2, numleafs * sizeof( dleafBSP2_t ));
	AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertex_t ));
	AddLump( LUMP_NODES, dnodesBSP2, numnodes * sizeof( dnodeBSP2_t ));
	AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo_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_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[ 0 ] ));
	AddLump( LUMP_EDGES, dedgesBSP2, numedges * sizeof( dedgeBSP2_t ));
	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 );

	fseek( wadfile, 0, SEEK_SET );
	SafeWrite( wadfile, header, sizeof( dheader_t ));
	fclose( wadfile );
}
#endif

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

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

Dumps info about current file
=============
*/
void
PrintBSPFileSizes( void ) {
	printf( "%6i planes      %7i\n"
		, numplanes, (int) ( numplanes * sizeof( dplane_t )));
	printf( "%6i vertexes    %7i\n"
		, numvertexes, (int) ( numvertexes * sizeof( dvertex_t )));
	printf( "%6i nodes       %7i\n"
		, numnodes, (int) ( numnodes * sizeof( dnode_t )));
	printf( "%6i texinfo     %7i\n"
		, numtexinfo, (int) ( numtexinfo * sizeof( texinfo_t )));
	printf( "%6i faces       %7i\n"
		, numfaces, (int) ( numfaces * sizeof( dface_t )));
	printf( "%6i clipnodes   %7i\n"
		, numclipnodes, (int) ( numclipnodes * sizeof( dclipnode_t )));
	printf( "%6i leafs       %7i\n"
		, numleafs, (int) ( numleafs * sizeof( dleaf_t )));
	printf( "%6i marksurfaces%7i\n"
		, nummarksurfaces, (int) ( nummarksurfaces * sizeof( dmarksurfaces[ 0 ] )));
	printf( "%6i surfedges   %7i\n"
		, numsurfedges, (int) ( numsurfedges * sizeof( dsurfedges[ 0 ] )));
	printf( "%6i edges       %7i\n"
		, numedges, (int) ( numedges * sizeof( dedge_t )));
	printf( "%6i models      %7i\n"
		, nummodels, (int) ( nummodels * sizeof( dmodel_t )));

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