summaryrefslogtreecommitdiff
path: root/source/bsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/bsp.c')
-rw-r--r--source/bsp.c1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/source/bsp.c b/source/bsp.c
new file mode 100644
index 0000000..2a30dc2
--- /dev/null
+++ b/source/bsp.c
@@ -0,0 +1,1179 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+Copyright (C) 2008 Andrey Nazarov
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// bsp.c -- model loading
+
+#include "com_local.h"
+#include "q_list.h"
+#include "files.h"
+#include "sys_public.h"
+#include "bsp.h"
+
+void BSP_SetError( const char *fmt, ... ) q_printf( 1, 2 );
+
+extern mtexinfo_t nulltexinfo;
+
+static cvar_t *map_override;
+
+/*
+===============================================================================
+
+ LUMP LOADING
+
+===============================================================================
+*/
+
+#define BSP_Malloc( size ) Hunk_Alloc( &bsp->pool, size )
+
+#define LOAD( Func ) \
+ static qboolean BSP_Load##Func( bsp_t *bsp, void *base, size_t count )
+
+/*
+=================
+Visibility
+=================
+*/
+LOAD( Visibility ) {
+ unsigned numclusters, bitofs;
+ int i, j;
+
+ if( !count ) {
+ return qtrue;
+ }
+
+ bsp->numvisibility = count;
+ bsp->vis = BSP_Malloc( count );
+ memcpy( bsp->vis, base, count );
+
+ numclusters = LittleLong( bsp->vis->numclusters );
+ if( numclusters > ( count - 4 ) / 8 ) {
+ BSP_SetError( "%s: bad numclusters", __func__ );
+ }
+ bsp->vis->numclusters = numclusters;
+ bsp->visrowsize = ( numclusters + 7 ) >> 3;
+ for( i = 0; i < numclusters; i++ ) {
+ for( j = 0; j < 2; j++ ) {
+ bitofs = LittleLong( bsp->vis->bitofs[i][j] );
+ if( bitofs >= count ) {
+ BSP_SetError( "%s: bad bitofs", __func__ );
+ return qfalse;
+ }
+ bsp->vis->bitofs[i][j] = bitofs;
+ }
+ }
+
+ return qtrue;
+}
+
+/*
+=================
+Texinfo
+=================
+*/
+LOAD( Texinfo ) {
+ dtexinfo_t *in;
+ mtexinfo_t *out;
+ int i;
+#if USE_REF
+ int j, k;
+ int next;
+ mtexinfo_t *step;
+#endif
+
+ bsp->numtexinfo = count;
+ bsp->texinfo = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->texinfo;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ memcpy( out->c.name, in->texture, sizeof( out->c.name ) );
+ out->c.name[ sizeof( out->c.name ) - 1 ] = 0;
+ memcpy( out->name, in->texture, sizeof( out->name ) );
+ out->name[ sizeof( out->name ) - 1 ] = 0;
+ out->c.flags = LittleLong (in->flags);
+ out->c.value = LittleLong (in->value);
+
+#if USE_REF
+ for( j = 0; j < 2; j++ ) {
+ for( k = 0; k < 3; k++ ) {
+ out->axis[j][k] = LittleFloat( in->vecs[j][k] );
+ }
+ out->offset[j] = LittleFloat( in->vecs[j][k] );
+ }
+
+ next = LittleLong( in->nexttexinfo );
+ if( next > 0 ) {
+ if( next >= count ) {
+ BSP_SetError( "%s: bad anim chain", __func__ );
+ return qfalse;
+ }
+ out->next = bsp->texinfo + next;
+ } else {
+ out->next = NULL;
+ }
+#endif
+ }
+
+#if USE_REF
+ // count animation frames
+ out = bsp->texinfo;
+ for( i = 0; i < count; i++, out++ ) {
+ out->numframes = 1;
+ for( step = out->next; step && step != out; step = step->next ) {
+ out->numframes++;
+ }
+ }
+#endif
+ return qtrue;
+}
+
+/*
+=================
+Planes
+=================
+*/
+LOAD( Planes ) {
+ dplane_t *in;
+ cplane_t *out;
+ int i, j;
+
+ bsp->numplanes = count;
+ bsp->planes = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->planes;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ for( j = 0; j < 3; j++ ) {
+ out->normal[j] = LittleFloat( in->normal[j] );
+ }
+ out->dist = LittleFloat( in->dist );
+ SetPlaneType( out );
+ SetPlaneSignbits( out );
+ }
+ return qtrue;
+}
+
+/*
+=================
+BrushSides
+=================
+*/
+LOAD( BrushSides ) {
+ dbrushside_t *in;
+ mbrushside_t *out;
+ int i;
+ unsigned planenum, texinfo;
+
+ bsp->numbrushsides = count;
+ bsp->brushsides = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->brushsides;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ planenum = LittleShort (in->planenum);
+ if( planenum >= bsp->numplanes ) {
+ BSP_SetError( "%s: bad planenum", __func__ );
+ return qfalse;
+ }
+ out->plane = bsp->planes + planenum;
+ texinfo = LittleShort (in->texinfo);
+ if( texinfo == ( uint16_t )-1 ) {
+ out->texinfo = &nulltexinfo;
+ } else {
+ if (texinfo >= bsp->numtexinfo) {
+ BSP_SetError( "%s: bad texinfo", __func__ );
+ return qfalse;
+ }
+ out->texinfo = bsp->texinfo + texinfo;
+ }
+ }
+ return qtrue;
+}
+
+/*
+=================
+Brushes
+=================
+*/
+LOAD( Brushes ) {
+ dbrush_t *in;
+ mbrush_t *out;
+ int i;
+ unsigned firstside, numsides, lastside;
+
+ bsp->numbrushes = count;
+ bsp->brushes = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->brushes;
+ for( i = 0; i < count; i++, out++, in++ ) {
+ firstside = LittleLong(in->firstside);
+ numsides = LittleLong(in->numsides);
+ lastside = firstside + numsides;
+ if( lastside < firstside || lastside > bsp->numbrushsides ) {
+ BSP_SetError( "%s: bad brushsides", __func__ );
+ return qfalse;
+ }
+ out->firstbrushside = bsp->brushsides + firstside;
+ out->numsides = numsides;
+ out->contents = LittleLong(in->contents);
+ out->checkcount = 0;
+ }
+ return qtrue;
+}
+
+/*
+=================
+LeafBrushes
+=================
+*/
+LOAD( LeafBrushes ) {
+ uint16_t *in;
+ mbrush_t **out;
+ int i;
+ unsigned brushnum;
+
+ bsp->numleafbrushes = count;
+ bsp->leafbrushes = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->leafbrushes;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ brushnum = LittleShort( *in );
+ if( brushnum >= bsp->numbrushes ) {
+ BSP_SetError( "%s: bad brushnum", __func__ );
+ return qfalse;
+ }
+ *out = bsp->brushes + brushnum;
+ }
+ return qtrue;
+}
+
+
+#if USE_REF
+/*
+=================
+Lightmap
+=================
+*/
+LOAD( Lightmap ) {
+#if USE_REF == REF_SOFT
+ byte *in;
+ byte *out;
+ int i;
+
+ count /= 3;
+#endif
+
+ if( !count ) {
+ return qtrue;
+ }
+
+ bsp->numlightmapbytes = count;
+ bsp->lightmap = BSP_Malloc( count );
+
+#if USE_REF == REF_SOFT
+ // convert the 24 bit lighting down to 8 bit
+ // by taking the brightest component
+ in = base;
+ out = bsp->lightmap;
+ for( i = 0; i < count; i++, in += 3, out++ ) {
+ if( in[0] > in[1] && in[0] > in[2] )
+ *out = in[0];
+ else if( in[1] > in[0] && in[1] > in[2] )
+ *out = in[1];
+ else
+ *out = in[2];
+ }
+#else
+ memcpy( bsp->lightmap, base, count );
+#endif
+ return qtrue;
+}
+
+/*
+=================
+Vertices
+=================
+*/
+LOAD( Vertices ) {
+ dvertex_t *in;
+ mvertex_t *out;
+ int i, j;
+
+ bsp->numvertices = count;
+ bsp->vertices = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->vertices;
+ for( i = 0; i < count; i++, out++, in++ ) {
+ for( j = 0; j < 3; j++ ) {
+ out->point[j] = LittleFloat( in->point[j] );
+ }
+ }
+ return qtrue;
+}
+
+/*
+=================
+Edges
+=================
+*/
+LOAD( Edges ) {
+ dedge_t *in;
+ medge_t *out;
+ int i, j;
+ unsigned vertnum;
+
+ bsp->numedges = count;
+ bsp->edges = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->edges;
+ for( i = 0; i < count; i++, out++, in++ ) {
+ for( j = 0; j < 2; j++ ) {
+ vertnum = LittleShort( in->v[j] );
+ if( vertnum >= bsp->numvertices ) {
+ BSP_SetError( "%s: bad vertnum", __func__ );
+ return qfalse;
+ }
+ out->v[j] = bsp->vertices + vertnum;
+ }
+ }
+ return qtrue;
+}
+
+/*
+=================
+SurfEdges
+=================
+*/
+LOAD( SurfEdges ) {
+ int *in;
+ msurfedge_t *out;
+ int i;
+ int index, vert;
+
+ bsp->numsurfedges = count;
+ bsp->surfedges = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->surfedges;
+ for( i = 0; i < count; i++, out++, in++ ) {
+ index = ( signed int )LittleLong( *in );
+
+ vert = 0;
+ if( index < 0 ) {
+ index = -index;
+ vert = 1;
+ }
+
+ if( index >= bsp->numedges ) {
+ BSP_SetError( "%s: bad edgenum", __func__ );
+ }
+
+ out->edge = bsp->edges + index;
+ out->vert = vert;
+ }
+ return qtrue;
+}
+
+/*
+=================
+Faces
+=================
+*/
+LOAD( Faces ) {
+ dface_t *in;
+ mface_t *out;
+ int i;
+#if USE_REF == REF_SOFT
+ int j;
+#endif
+ unsigned texinfo, lightofs;
+ unsigned firstedge, numedges, lastedge;
+ unsigned planenum, side;
+
+ bsp->numfaces = count;
+ bsp->faces = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->faces;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ firstedge = LittleLong( in->firstedge );
+ numedges = LittleShort( in->numedges );
+ lastedge = firstedge + numedges;
+ if( numedges < 3 || lastedge < firstedge || lastedge > bsp->numsurfedges ) {
+ BSP_SetError( "%s: bad surfedges", __func__ );
+ return qfalse;
+ }
+ out->firstsurfedge = bsp->surfedges + firstedge;
+ out->numsurfedges = numedges;
+
+ planenum = LittleShort( in->planenum );
+ if( planenum >= bsp->numplanes ) {
+ BSP_SetError( "%s: bad planenum", __func__ );
+ return qfalse;
+ }
+ out->plane = bsp->planes + planenum;
+
+ texinfo = LittleShort( in->texinfo );
+ if( texinfo >= bsp->numtexinfo ) {
+ BSP_SetError( "%s: bad texinfo", __func__ );
+ return qfalse;
+ }
+ out->texinfo = bsp->texinfo + texinfo;
+
+#if USE_REF == REF_SOFT
+ for( j = 0; j < MAX_LIGHTMAPS; j++ ) {
+ out->styles[j] = in->styles[j];
+ }
+#endif
+
+ lightofs = LittleLong( in->lightofs );
+ if( lightofs == ( uint32_t )-1 || bsp->numlightmapbytes == 0 ) {
+ out->lightmap = NULL;
+ } else {
+#if USE_REF == REF_SOFT
+ // lighting info is converted from 24 bit on disk to 8 bit
+ lightofs /= 3;
+#endif
+ if( lightofs >= bsp->numlightmapbytes ) {
+ BSP_SetError( "%s: bad lightofs", __func__ );
+ return qfalse;
+ }
+ out->lightmap = bsp->lightmap + lightofs;
+ }
+
+ side = LittleShort( in->side );
+ out->drawflags = side & DSURF_PLANEBACK;
+ }
+ return qtrue;
+}
+
+/*
+=================
+LeafFaces
+=================
+*/
+LOAD( LeafFaces ) {
+ uint16_t *in;
+ mface_t **out;
+ int i;
+ unsigned facenum;
+
+ bsp->numleaffaces = count;
+ bsp->leaffaces = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->leaffaces;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ facenum = LittleShort( *in );
+ if( facenum >= bsp->numfaces ) {
+ BSP_SetError( "%s: bad facenum", __func__ );
+ return qfalse;
+ }
+ *out = bsp->faces + facenum;
+ }
+ return qtrue;
+}
+#endif
+
+/*
+=================
+Leafs
+=================
+*/
+LOAD( Leafs ) {
+ dleaf_t *in;
+ mleaf_t *out;
+ int i, cluster;
+ unsigned area, firstleafbrush, numleafbrushes;
+#if USE_REF
+ int j;
+ unsigned firstleafface, numleaffaces;
+#endif
+
+ bsp->numleafs = count;
+ bsp->leafs = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->leafs;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ out->plane = NULL;
+ out->contents = LittleLong (in->contents);
+ cluster = ( signed short )LittleShort (in->cluster);
+ if( bsp->vis && cluster != -1 ) {
+ if( cluster < 0 || cluster >= bsp->vis->numclusters ) {
+ BSP_SetError( "%s: bad cluster", __func__ );
+ return qfalse;
+ }
+ }
+ out->cluster = cluster;
+
+ area = LittleShort( in->area );
+ if( area >= bsp->numareas ) {
+ BSP_SetError( "%s: bad area", __func__ );
+ return qfalse;
+ }
+ out->area = area;
+
+ firstleafbrush = LittleShort (in->firstleafbrush);
+ numleafbrushes = LittleShort (in->numleafbrushes);
+ if( firstleafbrush + numleafbrushes > bsp->numleafbrushes ) {
+ BSP_SetError( "%s: bad leafbrushes", __func__ );
+ return qfalse;
+ }
+ out->firstleafbrush = bsp->leafbrushes + firstleafbrush;
+ out->numleafbrushes = numleafbrushes;
+
+#if USE_REF
+ firstleafface = LittleShort (in->firstleafface);
+ numleaffaces = LittleShort (in->numleaffaces);
+ if( firstleafface + numleaffaces > bsp->numleaffaces ) {
+ BSP_SetError( "%s: bad leaffaces", __func__ );
+ return qfalse;
+ }
+ out->firstleafface = bsp->leaffaces + firstleafface;
+ out->numleaffaces = numleaffaces;
+
+ for( j = 0; j < 3; j++ ) {
+ out->mins[j] = ( signed short )LittleShort( in->mins[j] );
+ out->maxs[j] = ( signed short )LittleShort( in->maxs[j] );
+ }
+
+ out->parent = NULL;
+ out->visframe = -1;
+#endif
+ }
+
+ if (bsp->leafs[0].contents != CONTENTS_SOLID) {
+ BSP_SetError( "%s: map leaf 0 is not CONTENTS_SOLID", __func__ );
+ return qfalse;
+ }
+ return qtrue;
+}
+
+/*
+=================
+Nodes
+=================
+*/
+LOAD( Nodes ) {
+ dnode_t *in;
+ uint32_t child;
+ mnode_t *out;
+ int i, j;
+ unsigned planeNum;
+#if USE_REF
+ unsigned firstface, numfaces, lastface;
+#endif
+
+ if( !count ) {
+ BSP_SetError( "%s: map with no nodes", __func__ );
+ return qfalse;
+ }
+
+ bsp->numnodes = count;
+ bsp->nodes = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->nodes;
+ for( i = 0; i < count; i++, out++, in++ ) {
+ planeNum = LittleLong(in->planenum);
+ if( planeNum >= bsp->numplanes ) {
+ BSP_SetError( "%s: bad planenum", __func__ );
+ return qfalse;
+ }
+ out->plane = bsp->planes + planeNum;
+
+ for( j = 0; j < 2; j++ ) {
+ child = LittleLong( in->children[j] );
+ if( child & 0x80000000 ) {
+ child = ~child;
+ if( child >= bsp->numleafs ) {
+ BSP_SetError( "%s: bad leafnum", __func__ );
+ return qfalse;
+ }
+ out->children[j] = ( mnode_t * )( bsp->leafs + child );
+ } else {
+ if( child >= count ) {
+ BSP_SetError( "%s: bad nodenum", __func__ );
+ return qfalse;
+ }
+ out->children[j] = bsp->nodes + child;
+ }
+ }
+
+#if USE_REF
+ firstface = LittleLong( in->firstface );
+ numfaces = LittleLong( in->numfaces );
+ lastface = firstface + numfaces;
+ if( lastface < firstface || lastface > bsp->numfaces ) {
+ BSP_SetError( "%s: bad faces", __func__ );
+ return qfalse;
+ }
+ out->firstface = bsp->faces + firstface;
+ out->numfaces = numfaces;
+
+ for( j = 0; j < 3; j++ ) {
+ out->mins[j] = ( signed short )LittleShort( in->mins[j] );
+ out->maxs[j] = ( signed short )LittleShort( in->maxs[j] );
+ }
+
+ out->parent = NULL;
+ out->visframe = -1;
+#endif
+ }
+ return qtrue;
+}
+
+/*
+=================
+Submodels
+=================
+*/
+LOAD( Submodels ) {
+ dmodel_t *in;
+ mmodel_t *out;
+ int i, j;
+ unsigned headnode;
+#if USE_REF
+ unsigned firstface, numfaces, lastface;
+#endif
+
+ bsp->models = BSP_Malloc( sizeof( *out ) * count );
+ bsp->nummodels = count;
+
+ in = base;
+ out = bsp->models;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ for( j = 0; j < 3; j++ ) {
+ // spread the mins / maxs by a pixel
+ out->mins[j] = LittleFloat (in->mins[j]) - 1;
+ out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
+ out->origin[j] = LittleFloat (in->origin[j]);
+ }
+ headnode = LittleLong (in->headnode);
+ if( headnode >= bsp->numnodes ) {
+ // FIXME: headnode may be garbage for some models
+ Com_DPrintf( "%s: bad headnode\n", __func__ );
+ out->headnode = NULL;
+ } else {
+ out->headnode = bsp->nodes + headnode;
+ }
+#if USE_REF
+ firstface = LittleLong( in->firstface );
+ numfaces = LittleLong( in->numfaces );
+ lastface = firstface + numfaces;
+ if( lastface < firstface || lastface > bsp->numfaces ) {
+ BSP_SetError( "%s: bad faces", __func__ );
+ return qfalse;
+ }
+ out->firstface = bsp->faces + firstface;
+ out->numfaces = numfaces;
+
+ out->radius = RadiusFromBounds( out->mins, out->maxs );
+#endif
+ }
+ return qtrue;
+}
+
+/*
+=================
+AreaPortals
+=================
+*/
+LOAD( AreaPortals ) {
+ dareaportal_t *in;
+ mareaportal_t *out;
+ int i;
+ unsigned portalnum, otherarea;
+
+ bsp->numareaportals = count;
+ bsp->areaportals = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->areaportals;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ portalnum = LittleLong (in->portalnum);
+ if( portalnum >= MAX_MAP_AREAPORTALS ) {
+ BSP_SetError( "%s: bad portalnum", __func__ );
+ return qfalse;
+ }
+ out->portalnum = portalnum;
+ otherarea = LittleLong (in->otherarea);
+ if( otherarea >= MAX_MAP_AREAS ) {
+ BSP_SetError( "%s: bad otherarea", __func__ );
+ return qfalse;
+ }
+ out->otherarea = otherarea;
+ }
+ return qtrue;
+}
+
+/*
+=================
+Areas
+=================
+*/
+LOAD( Areas ) {
+ darea_t *in;
+ marea_t *out;
+ int i;
+ unsigned numareaportals, firstareaportal, lastareaportal;
+
+ bsp->numareas = count;
+ bsp->areas = BSP_Malloc( sizeof( *out ) * count );
+
+ in = base;
+ out = bsp->areas;
+ for( i = 0; i < count; i++, in++, out++ ) {
+ numareaportals = LittleLong (in->numareaportals);
+ firstareaportal = LittleLong (in->firstareaportal);
+ lastareaportal = firstareaportal + numareaportals;
+ if( lastareaportal < firstareaportal || lastareaportal > bsp->numareaportals ) {
+ BSP_SetError( "%s: bad areaportals", __func__ );
+ return qfalse;
+ }
+ out->numareaportals = numareaportals;
+ out->firstareaportal = firstareaportal;
+ out->floodvalid = 0;
+ }
+ return qtrue;
+}
+
+/*
+=================
+EntityString
+=================
+*/
+LOAD( EntityString ) {
+ // optionally load the entity string from external source
+ if( map_override->integer ) {
+ char buffer[MAX_QPATH], *str;
+ size_t len;
+
+ COM_StripExtension( bsp->name, buffer, sizeof( buffer ) );
+ Q_strcat( buffer, sizeof( buffer ), ".ent" );
+ len = FS_LoadFile( buffer, ( void ** )&str );
+ if( str ) {
+ Com_DPrintf( "Loaded entity string from %s\n", buffer );
+ bsp->entitystring = BSP_Malloc( len + 1 );
+ memcpy( bsp->entitystring, str, len + 1 );
+ bsp->numentitychars = len;
+ FS_FreeFile( str );
+ return qtrue;
+ }
+ }
+
+ bsp->numentitychars = count;
+ bsp->entitystring = BSP_Malloc( count + 1 );
+ memcpy( bsp->entitystring, base, count );
+ bsp->entitystring[count] = 0;
+ return qtrue;
+}
+
+
+/*
+===============================================================================
+
+ MAP LOADING
+
+===============================================================================
+*/
+
+typedef struct {
+ qboolean (*load)( bsp_t *, void *, size_t );
+ int lump;
+ size_t size;
+ size_t maxcount;
+} lump_info_t;
+
+#define LUMP( Func, Lump, Type ) \
+ { BSP_Load##Func, LUMP_##Lump, sizeof( Type ), MAX_MAP_##Lump }
+
+static const lump_info_t bsp_lumps[] = {
+ LUMP( Visibility, VISIBILITY, byte ),
+ LUMP( Texinfo, TEXINFO, dtexinfo_t ),
+ LUMP( Planes, PLANES, dplane_t ),
+ LUMP( BrushSides, BRUSHSIDES, dbrushside_t ),
+ LUMP( Brushes, BRUSHES, dbrush_t ),
+ LUMP( LeafBrushes, LEAFBRUSHES, uint16_t ),
+ LUMP( AreaPortals, AREAPORTALS, dareaportal_t ),
+ LUMP( Areas, AREAS, darea_t ),
+#if USE_REF
+ LUMP( Lightmap, LIGHTING, byte ),
+ LUMP( Vertices, VERTEXES, dvertex_t ),
+ LUMP( Edges, EDGES, dedge_t ),
+ LUMP( SurfEdges, SURFEDGES, uint32_t ),
+ LUMP( Faces, FACES, dface_t ),
+ LUMP( LeafFaces, LEAFFACES, uint16_t ),
+#endif
+ LUMP( Leafs, LEAFS, dleaf_t ),
+ LUMP( Nodes, NODES, dnode_t ),
+ LUMP( Submodels, MODELS, dmodel_t ),
+ LUMP( EntityString, ENTSTRING, char ),
+ { NULL }
+};
+
+static list_t bsp_cache;
+static char bsp_error[MAX_QPATH];
+
+static void BSP_List_f( void ) {
+ bsp_t *bsp;
+ size_t bytes;
+
+ if( LIST_EMPTY( &bsp_cache ) ) {
+ Com_Printf( "BSP cache is empty\n" );
+ return;
+ }
+
+ Com_Printf( "------------------\n");
+ bytes = 0;
+
+ LIST_FOR_EACH( bsp_t, bsp, &bsp_cache, entry ) {
+ Com_Printf( "%8"PRIz" : %s (%d refs)\n",
+ bsp->pool.mapped, bsp->name, bsp->refcount );
+ bytes += bsp->pool.mapped;
+ }
+ Com_Printf( "Total resident: %"PRIz"\n", bytes );
+}
+
+static bsp_t *BSP_Find( const char *name ) {
+ bsp_t *bsp;
+
+ LIST_FOR_EACH( bsp_t, bsp, &bsp_cache, entry ) {
+ if( !FS_pathcmp( bsp->name, name ) ) {
+ return bsp;
+ }
+ }
+ return NULL;
+}
+
+static qboolean BSP_SetParent( mnode_t *node ) {
+ mnode_t *child;
+
+ while( node->plane ) {
+ child = node->children[0];
+ if( child->parent ) {
+ return qfalse;
+ }
+ child->parent = node;
+ BSP_SetParent( child );
+
+ child = node->children[1];
+ if( child->parent ) {
+ return qfalse;
+ }
+ child->parent = node;
+ node = child;
+ }
+ return qtrue;
+}
+
+void BSP_SetError( const char *fmt, ... ) {
+ va_list argptr;
+
+ va_start( argptr, fmt );
+ Q_vsnprintf( bsp_error, sizeof( bsp_error ), fmt, argptr );
+ va_end( argptr );
+}
+
+const char *BSP_GetError( void ) {
+ return bsp_error;
+}
+
+void BSP_Free( bsp_t *bsp ) {
+ if( !bsp ) {
+ return;
+ }
+ if( bsp->refcount <= 0 ) {
+ Com_Error( ERR_FATAL, "%s: negative refcount", __func__ );
+ }
+ if( --bsp->refcount == 0 ) {
+ Hunk_Free( &bsp->pool );
+ List_Remove( &bsp->entry );
+ Z_Free( bsp );
+ }
+}
+
+
+/*
+==================
+BSP_Load
+
+Loads in the map and all submodels
+==================
+*/
+bsp_t *BSP_Load( const char *name ) {
+ bsp_t *bsp;
+ byte *buf;
+ dheader_t *header;
+ const lump_info_t *info;
+ size_t filelen, ofs, len, end, count;
+
+ if( !name || !name[0] ) {
+ Com_Error( ERR_FATAL, "%s: NULL", __func__ );
+ }
+
+ BSP_SetError( "no error" );
+
+ if( ( bsp = BSP_Find( name ) ) != NULL ) {
+ bsp->refcount++;
+ return bsp;
+ }
+
+
+ //
+ // load the file
+ //
+ filelen = FS_LoadFile( name, ( void ** )&buf );
+ if( !buf ) {
+ BSP_SetError( "file not found" );
+ return NULL;
+ }
+
+ len = strlen( name );
+ bsp = Z_Mallocz( sizeof( *bsp ) + len );
+ memcpy( bsp->name, name, len + 1 );
+ bsp->refcount = 1;
+
+ // byte swap and validate the header
+ header = ( dheader_t * )buf;
+ if( LittleLong( header->ident ) != IDBSPHEADER ) {
+ BSP_SetError( "not an IBSP file" );
+ goto fail2;
+ }
+ if( LittleLong( header->version ) != BSPVERSION ) {
+ BSP_SetError( "unsupported IBSP version" );
+ goto fail2;
+ }
+
+ // load into hunk
+ Hunk_Begin( &bsp->pool, 0x1000000 );
+
+ // calculate the checksum
+ bsp->checksum = LittleLong( Com_BlockChecksum( buf, filelen ) );
+
+ // byte swap, validate and load all lumps
+ for( info = bsp_lumps; info->load; info++ ) {
+ ofs = LittleLong( header->lumps[info->lump].fileofs );
+ len = LittleLong( header->lumps[info->lump].filelen );
+ end = ofs + len;
+ if( end < ofs || end > filelen ) {
+ BSP_SetError( "lump %d extents are out of bounds", info->lump );
+ goto fail1;
+ }
+ if( len % info->size ) {
+ BSP_SetError( "lump %d has funny size", info->lump );
+ goto fail1;
+ }
+ count = len / info->size;
+ if( count > info->maxcount ) {
+ BSP_SetError( "lump %d has too many elements", info->lump );
+ goto fail1;
+ }
+ if( !info->load( bsp, buf + ofs, count ) ) {
+ goto fail1;
+ }
+ }
+
+ if( !BSP_SetParent( bsp->nodes ) ) {
+ BSP_SetError( "cycle encountered in BSP graph" );
+ goto fail1;
+ }
+
+ Hunk_End( &bsp->pool );
+
+ FS_FreeFile( buf );
+
+ List_Append( &bsp_cache, &bsp->entry );
+ return bsp;
+
+fail1:
+ Hunk_Free( &bsp->pool );
+fail2:
+ FS_FreeFile( buf );
+ Z_Free( bsp );
+ return NULL;
+}
+
+/*
+===============================================================================
+
+HELPER FUNCTIONS
+
+===============================================================================
+*/
+
+#if USE_REF
+
+mface_t *BSP_LightPoint( mnode_t *node, vec3_t start, vec3_t end, int *ps, int *pt ) {
+ vec_t startFrac, endFrac, midFrac;
+ vec3_t _start, mid;
+ int side;
+ mface_t *surf;
+ mtexinfo_t *texinfo;
+ int i;
+ int s, t;
+
+ VectorCopy( start, _start );
+ while( node->plane ) {
+ // calculate distancies
+ startFrac = PlaneDiffFast( _start, node->plane );
+ endFrac = PlaneDiffFast( end, node->plane );
+ side = ( startFrac < 0 );
+
+ if( ( endFrac < 0 ) == side ) {
+ // both points are one the same side
+ node = node->children[side];
+ continue;
+ }
+
+ // find crossing point
+ midFrac = startFrac / ( startFrac - endFrac );
+ LerpVector( _start, end, midFrac, mid );
+
+ // check near side
+ surf = BSP_LightPoint( node->children[side], _start, mid, ps, pt );
+ if( surf ) {
+ return surf;
+ }
+
+ for( i = 0, surf = node->firstface; i < node->numfaces; i++, surf++ ) {
+ texinfo = surf->texinfo;
+ if( texinfo->c.flags & SURF_NOLM_MASK ) {
+ continue;
+ }
+ if( !surf->lightmap ) {
+ continue;
+ }
+ s = DotProduct( texinfo->axis[0], mid ) + texinfo->offset[0];
+ t = DotProduct( texinfo->axis[1], mid ) + texinfo->offset[1];
+
+ s -= surf->texturemins[0];
+ t -= surf->texturemins[1];
+ if( s < 0 || t < 0 ) {
+ continue;
+ }
+ if( s > surf->extents[0] || t > surf->extents[1] ) {
+ continue;
+ }
+
+ *ps = s;
+ *pt = t;
+
+ return surf;
+ }
+
+ // check far side
+ VectorCopy( mid, _start );
+ node = node->children[side^1];
+ }
+
+ return NULL;
+}
+
+#endif
+
+byte *BSP_ClusterVis( bsp_t *bsp, byte *mask, int cluster, int vis ) {
+ byte *in, *out;
+ int c;
+
+ if( !bsp || !bsp->vis ) {
+ return memset( mask, 0xff, MAX_MAP_VIS );
+ }
+ if( cluster == -1 ) {
+ return memset( mask, 0, bsp->visrowsize );
+ }
+ if( cluster < 0 || cluster >= bsp->vis->numclusters ) {
+ Com_Error( ERR_DROP, "%s: bad cluster", __func__ );
+ }
+
+ // decompress vis
+ in = ( byte * )bsp->vis + bsp->vis->bitofs[cluster][vis];
+ out = mask;
+ do {
+ if( *in ) {
+ *out++ = *in++;
+ continue;
+ }
+
+ c = in[1];
+ in += 2;
+ if( ( out - mask ) + c > bsp->visrowsize ) {
+ c = bsp->visrowsize - ( out - mask );
+ Com_WPrintf( "%s: overrun\n", __func__ );
+ }
+ while( c ) {
+ *out++ = 0;
+ c--;
+ }
+ } while( out - mask < bsp->visrowsize );
+
+ return mask;
+}
+
+mleaf_t *BSP_PointLeaf( mnode_t *node, vec3_t p ) {
+ float d;
+
+ while( node->plane ) {
+ d = PlaneDiffFast( p, node->plane );
+ if( d < 0 )
+ node = node->children[1];
+ else
+ node = node->children[0];
+ }
+
+ return ( mleaf_t * )node;
+}
+
+/*
+==================
+BSP_InlineModel
+==================
+*/
+mmodel_t *BSP_InlineModel( bsp_t *bsp, const char *name ) {
+ int num;
+
+ if( !bsp || !name ) {
+ Com_Error( ERR_DROP, "%s: NULL", __func__ );
+ }
+ if( name[0] != '*' ) {
+ Com_Error( ERR_DROP, "%s: bad name: %s", __func__, name );
+ }
+ num = atoi( name + 1 );
+ if( num < 1 || num >= bsp->nummodels ) {
+ Com_Error ( ERR_DROP, "%s: bad number: %d", __func__, num );
+ }
+
+ return &bsp->models[num];
+}
+
+void BSP_Init( void ) {
+ map_override = Cvar_Get( "map_override", "0", 0 );
+
+ Cmd_AddCommand( "bsplist", BSP_List_f );
+
+ List_Init( &bsp_cache );
+}
+