summaryrefslogtreecommitdiff
path: root/source/r_bsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/r_bsp.c')
-rw-r--r--source/r_bsp.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/source/r_bsp.c b/source/r_bsp.c
new file mode 100644
index 0000000..4f31803
--- /dev/null
+++ b/source/r_bsp.c
@@ -0,0 +1,763 @@
+/*
+Copyright (C) 2003-2006 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.
+
+*/
+
+//
+// r_bsp.c -- map loading functions common for both renderers
+//
+
+#include "config.h"
+#include "q_shared.h"
+#include "com_public.h"
+#include "q_files.h"
+#include "q_list.h"
+#include "r_shared.h"
+
+bspModel_t r_world;
+
+bspLeaf_t *Bsp_FindLeaf( vec3_t origin ) {
+ bspNode_t *node;
+ cplane_t *plane;
+ vec_t dot;
+
+ node = r_world.nodes;
+ do {
+ plane = node->plane;
+ dot = DotProduct( plane->normal, origin ) - plane->dist;
+ if( dot >= 0 ) {
+ node = node->children[0];
+ } else {
+ node = node->children[1];
+ }
+ } while( node->plane );
+
+ return ( bspLeaf_t * )node;
+}
+
+byte *Bsp_ClusterPVS( int clusterNum ) {
+ if( !r_world.vis || clusterNum == -1 ) {
+ return NULL;
+ }
+
+ return r_world.vis + clusterNum * r_world.rowsize;
+}
+
+
+/* ========================================================================= */
+
+#define Bsp_Malloc( size ) sys.HunkAlloc( &r_world.pool, size )
+
+static byte *loadData;
+
+static void Bsp_DecompressVis( byte *src, byte *dst, uint32 rowsize ) {
+ uint32 count;
+
+ do {
+ if( *src ) {
+ *dst++ = *src++;
+ rowsize--;
+ } else {
+ src++; count = *src++;
+ if( count > rowsize ) {
+ Com_WPrintf( "Bsp_DecompressVis: overrun\n" );
+ count = rowsize;
+ }
+ rowsize -= count;
+ while( count-- ) {
+ *dst++ = 0;
+ }
+ }
+ } while( rowsize );
+}
+
+static int Bsp_LoadVis( lump_t *lump ) {
+ dvis_t *src_vis;
+ byte *dst, *src;
+ uint32 numClusters, rowsize;
+ uint32 offset;
+ int i;
+
+ if( !lump->filelen ) {
+ r_world.vis = NULL;
+ r_world.numClusters = 0;
+ return 0;
+ }
+
+ if( lump->filelen < sizeof( *src ) ) {
+ Com_EPrintf( "LoadVis: bad lump size\n" );
+ return -1;
+ }
+
+ src_vis = ( dvis_t * )( loadData + lump->fileofs );
+ numClusters = LittleLong( src_vis->numclusters );
+ if( !numClusters ) {
+ r_world.vis = 0;
+ r_world.numClusters = 0;
+ return 0; /* it is OK to have a map without vis */
+ }
+ if( numClusters > MAX_MAP_LEAFS/8 ) {
+ Com_EPrintf( "LoadVis: bad number of clusters\n" );
+ return -1;
+ }
+
+ rowsize = ( numClusters + 7 ) >> 3;
+ r_world.numClusters = numClusters;
+ r_world.rowsize = rowsize;
+ r_world.vis = Bsp_Malloc( numClusters * rowsize );
+
+ dst = r_world.vis;
+ for( i = 0; i < numClusters; i++ ) {
+ offset = LittleLong( src_vis->bitofs[i][DVIS_PVS] );
+ if( offset >= lump->filelen ) {
+ Com_EPrintf( "LoadVis: bad offset\n" );
+ return -1;
+ }
+
+ src = ( byte * )src_vis + offset;
+ Bsp_DecompressVis( src, dst, rowsize );
+ dst += rowsize;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadVertices( lump_t *lump ) {
+ int count;
+ dvertex_t *src;
+ dvertex_t *dst;
+ int i;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadVertices: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.vertices = Bsp_Malloc( ( count + EXTRA_VERTICES ) * sizeof( *dst ) );
+ r_world.numVertices = count;
+
+ src = ( dvertex_t * )( loadData + lump->fileofs );
+ dst = r_world.vertices;
+ for( i = 0; i < count; i++ ) {
+ dst->point[0] = LittleFloat( src->point[0] );
+ dst->point[1] = LittleFloat( src->point[1] );
+ dst->point[2] = LittleFloat( src->point[2] );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadEdges( lump_t *lump ) {
+ int count;
+ dedge_t *src;
+ dedge_t *dst;
+ int i;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadEdges: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.edges = Bsp_Malloc( ( count + EXTRA_EDGES ) * sizeof( *dst ) );
+ r_world.numEdges = count;
+
+ src = ( dedge_t * )( loadData + lump->fileofs );
+ dst = r_world.edges;
+ for( i = 0; i < count; i++ ) {
+ dst->v[0] = LittleShort( src->v[0] );
+ dst->v[1] = LittleShort( src->v[1] );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadSurfEdges( lump_t *lump ) {
+ int count;
+ int *src;
+ int *dst;
+ int i;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadSurfEdges: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.surfEdges = Bsp_Malloc( ( count + EXTRA_SURFEDGES ) * sizeof( *dst ) );
+ r_world.numSurfEdges = count;
+
+ src = ( int * )( loadData + lump->fileofs );
+ dst = r_world.surfEdges;
+ for( i = 0; i < count; i++ ) {
+ *dst++ = LittleLong( *src++ );
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadTexinfo( lump_t *lump ) {
+ int count;
+ int i;
+ texinfo_t *src;
+ bspTexinfo_t *dst, *tex;
+ image_t *texture;
+ char path[MAX_QPATH];
+ int animNext;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadTexinfo: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numTexinfos = count;
+ r_world.texinfos = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( texinfo_t * )( loadData + lump->fileofs );
+ dst = r_world.texinfos;
+ for( i = 0; i < count; i++ ) {
+ Q_strncpyz( dst->name, src->texture, sizeof( dst->name ) );
+ dst->flags = LittleLong( src->flags );
+
+ dst->axis[0][0] = LittleFloat( src->vecs[0][0] );
+ dst->axis[0][1] = LittleFloat( src->vecs[0][1] );
+ dst->axis[0][2] = LittleFloat( src->vecs[0][2] );
+
+ dst->axis[1][0] = LittleFloat( src->vecs[1][0] );
+ dst->axis[1][1] = LittleFloat( src->vecs[1][1] );
+ dst->axis[1][2] = LittleFloat( src->vecs[1][2] );
+
+ dst->offset[0] = LittleFloat( src->vecs[0][3] );
+ dst->offset[1] = LittleFloat( src->vecs[1][3] );
+
+#ifdef OPENGL_RENDERER
+ upload_texinfo = dst;
+
+ VectorNormalize2( dst->axis[0], dst->normalizedAxis[0] );
+ VectorNormalize2( dst->axis[1], dst->normalizedAxis[1] );
+#endif
+
+ animNext = LittleLong( src->nexttexinfo );
+ if( animNext > 0 ) {
+ if( animNext >= count ) {
+ Com_EPrintf( "Bsp_LoadTexinfo: bad anim chain\n" );
+ return -1;
+ }
+ dst->animNext = r_world.texinfos + animNext;
+ } else {
+ dst->animNext = NULL;
+ }
+
+ Com_sprintf( path, sizeof( path ), "textures/%s.wal", dst->name );
+ texture = R_FindImage( path, it_wall );
+ if( texture ) {
+ dst->image = texture;
+ } else {
+ dst->image = r_notexture;
+ }
+
+#ifdef OPENGL_RENDERER
+ upload_texinfo = NULL;
+#endif
+
+ src++; dst++;
+ }
+
+ dst = r_world.texinfos;
+ for( i = 0; i < count; i++ ) {
+ dst->numFrames = 1;
+ for( tex = dst->animNext; tex && tex != dst; tex = tex->animNext ) {
+ dst->numFrames++;
+ }
+ dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadFaces( lump_t *lump ) {
+ int count;
+ dface_t *src_face;
+ bspSurface_t *dst_face;
+ int i, j;
+ uint32 texinfoNum;
+ uint32 lightmapOffset;
+ uint32 firstEdge, numEdges;
+ uint32 planeNum;
+
+ count = lump->filelen / sizeof( *src_face );
+ if( lump->filelen % sizeof( *src_face ) ) {
+ Com_EPrintf( "LoadFace: bad faces lump size\n" );
+ return -1;
+ }
+
+ r_world.surfaces = Bsp_Malloc( ( count + EXTRA_SURFACES ) * sizeof( *dst_face ) );
+ r_world.numSurfaces = count;
+
+ src_face = ( dface_t * )( loadData + lump->fileofs );
+ dst_face = r_world.surfaces;
+ for( i = 0; i < count; i++ ) {
+ firstEdge = LittleLong( src_face->firstedge );
+ numEdges = LittleShort( src_face->numedges );
+ if( numEdges < 3 || numEdges > MAX_MAP_SURFEDGES ) {
+ Com_EPrintf( "LoadFace: bad surfedges\n" );
+ return -1;
+ }
+ if( firstEdge + numEdges > r_world.numSurfEdges ) {
+ Com_EPrintf( "LoadFace: surfedges out of bounds\n" );
+ return -1;
+ }
+
+ planeNum = LittleShort( src_face->planenum );
+ if( planeNum >= r_world.numPlanes ) {
+ Com_EPrintf( "LoadFace: bad planenum\n" );
+ return -1;
+ }
+
+ texinfoNum = LittleShort( src_face->texinfo );
+ if( texinfoNum >= r_world.numTexinfos ) {
+ Com_EPrintf( "LoadFace: bad texinfo\n" );
+ return -1;
+ }
+
+ lightmapOffset = LittleLong( src_face->lightofs );
+ if( lightmapOffset == ( uint32 )-1 || r_world.lightmapSize == 0 ) {
+ dst_face->lightmap = NULL;
+ } else {
+ if( lightmapOffset >= r_world.lightmapSize ) {
+ Com_EPrintf( "LoadFace: bad lightofs\n" );
+ return -1;
+ }
+ dst_face->lightmap = r_world.lightmap + lightmapOffset;
+ }
+
+ dst_face->texinfo = r_world.texinfos + texinfoNum;
+ j = LittleShort( src_face->side );
+ dst_face->side = j & 1;
+ dst_face->index = i;
+ dst_face->type = DSURF_POLY;
+ dst_face->plane = r_world.planes + planeNum;
+ dst_face->firstSurfEdge = r_world.surfEdges + firstEdge;
+ dst_face->numSurfEdges = numEdges;
+
+#ifdef OPENGL_RENDERER
+ if( GL_PostProcessSurface( dst_face ) ) {
+ return -1;
+ }
+#endif
+
+ src_face++; dst_face++;
+ }
+
+
+ return 0;
+}
+
+static int Bsp_LoadLeafFaces( lump_t *lump ) {
+ int count;
+ int i;
+ uint32 faceNum;
+ uint16 *src;
+ bspSurface_t **dst;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadLeafFaces: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numLeafFaces = count;
+ r_world.leafFaces = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( uint16 * )( loadData + lump->fileofs );
+ dst = r_world.leafFaces;
+ for( i = 0; i < count; i++ ) {
+ faceNum = LittleShort( *src );
+ if( faceNum >= r_world.numSurfaces ) {
+ Com_EPrintf( "LoadLeafFaces: bad face index\n" );
+ return -1;
+ }
+ *dst = r_world.surfaces + faceNum;
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadLeafs( lump_t *lump ) {
+ int count;
+ int i;
+ uint32 cluster, area;
+ uint32 firstLeafFace;
+ uint32 numLeafFaces;
+ dleaf_t *src;
+ bspLeaf_t *dst;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadLeafs: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numLeafs = count;
+ r_world.leafs = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( dleaf_t * )( loadData + lump->fileofs );
+ dst = r_world.leafs;
+ for( i = 0; i < count; i++ ) {
+ dst->index = i;
+ dst->plane = NULL;
+ dst->parent = NULL;
+ dst->visframe = -1;
+
+ area = LittleShort( src->area );
+ if( area >= MAX_MAP_AREAS ) {
+ Com_EPrintf( "LoadLeafs: bad area num\n" );
+ return -1;
+ }
+ dst->area = area;
+ dst->contents = LittleLong( src->contents );
+
+ cluster = LittleShort( src->cluster );
+ if( cluster == ( uint16 )-1 || r_world.numClusters == 0 ) {
+ dst->cluster = -1;
+ } else {
+ if( cluster >= r_world.numClusters ) {
+ Com_EPrintf( "LoadLeafs: bad cluster num\n" );
+ return -1;
+ }
+ dst->cluster = cluster;
+ }
+
+ firstLeafFace = LittleShort( src->firstleafface );
+ numLeafFaces = LittleShort( src->numleaffaces );
+
+ if( firstLeafFace + numLeafFaces > r_world.numLeafFaces ) {
+ Com_EPrintf( "LoadLeafs: bad leafface\n" );
+ return -1;
+ }
+
+ dst->firstLeafFace = r_world.leafFaces + firstLeafFace;
+ dst->numLeafFaces = numLeafFaces;
+
+ LSV( mins );
+ LSV( maxs );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadPlanes( lump_t *lump ) {
+ int count;
+ int i;
+ dplane_t *src;
+ cplane_t *dst;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadPlanes: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numPlanes = count;
+ r_world.planes = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( dplane_t * )( loadData + lump->fileofs );
+ dst = r_world.planes;
+ for( i = 0; i < count; i++ ) {
+ LV( normal );
+ LF( dist );
+ SetPlaneType( dst );
+ SetPlaneSignbits( dst );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+static int Bsp_LoadNodes( lump_t *lump ) {
+ int count;
+ int i, j;
+ uint32 planenum;
+ uint32 child;
+ dnode_t *src;
+ bspNode_t *dst;
+ uint32 firstFace, numFaces;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadNodes: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numNodes = count;
+ r_world.nodes = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( dnode_t * )( loadData + lump->fileofs );
+ dst = r_world.nodes;
+ for( i = 0; i < count; i++ ) {
+ dst->index = i;
+ dst->parent = NULL;
+ dst->visframe = -1;
+
+ /* load splitting plane index */
+ planenum = LittleLong( src->planenum );
+ if( planenum >= r_world.numPlanes ) {
+ Com_EPrintf( "LoadNodes: bad planenum\n" );
+ return -1;
+ }
+ dst->plane = r_world.planes + planenum;
+
+ /* load children indices */
+ for( j = 0; j < 2; j++ ) {
+ child = LittleLong( src->children[j] );
+ if( child & 0x80000000 ) {
+ /* leaf index */
+ child = ~child;
+ if( child >= r_world.numLeafs ) {
+ Com_EPrintf( "LoadNodes: bad leafnum\n" );
+ return -1;
+ }
+ dst->children[j] = ( bspNode_t * )( r_world.leafs + child );
+ } else {
+ /* node index */
+ if( child >= count ) {
+ Com_EPrintf( "LoadNodes: bad nodenum\n" );
+ return -1;
+ }
+ dst->children[j] = r_world.nodes + child;
+ }
+ }
+
+ firstFace = LittleShort( src->firstface );
+ numFaces = LittleShort( src->numfaces );
+
+ if( firstFace + numFaces > r_world.numSurfaces ) {
+ Com_EPrintf( "LoadNodes: bad faces\n" );
+ return -1;
+ }
+
+ dst->firstFace = r_world.surfaces + firstFace;
+ dst->numFaces = numFaces;
+
+ LSV( mins );
+ LSV( maxs );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+#if 0
+int Bsp_LoadEntityString( lump_t *lump ) {
+ if( !lump->filelen ) {
+ return 0;
+ }
+
+ r_world.entityString = Bsp_Malloc( lump->filelen + 1 );
+ memcpy( r_world.entityString, loadData + lump->fileofs, lump->filelen );
+ r_world.entityString[lump->filelen] = 0;
+
+ return 0;
+}
+#endif
+
+int Bsp_LoadLightMap( lump_t *lump ) {
+ if( !lump->filelen ) {
+ return 0;
+ }
+
+ r_world.lightmap = Bsp_Malloc( lump->filelen );
+ memcpy( r_world.lightmap, loadData + lump->fileofs, lump->filelen );
+ r_world.lightmapSize = lump->filelen;
+
+ return 0;
+}
+
+static int Bsp_LoadSubmodels( lump_t *lump ) {
+ int count;
+ int i;
+ dmodel_t *src;
+ bspSubmodel_t *dst;
+ uint32 firstFace, numFaces;
+ uint32 headnode;
+
+ count = lump->filelen / sizeof( *src );
+ if( lump->filelen % sizeof( *src ) ) {
+ Com_EPrintf( "LoadSubmodels: bad lump size\n" );
+ return -1;
+ }
+
+ r_world.numSubmodels = count;
+ r_world.submodels = Bsp_Malloc( sizeof( *dst ) * count );
+
+ src = ( dmodel_t * )( loadData + lump->fileofs );
+ dst = r_world.submodels;
+ for( i = 0; i < count; i++ ) {
+ firstFace = LittleLong( src->firstface );
+ numFaces = LittleLong( src->numfaces );
+ if( firstFace + numFaces > r_world.numSurfaces ) {
+ Com_EPrintf( "LoadSubmodels: bad faces\n" );
+ return -1;
+ }
+
+ headnode = LittleLong( src->headnode );
+ if( headnode >= r_world.numNodes ) {
+ /* FIXME: headnode may be garbage for some models */
+ Com_DPrintf( "LoadSubmodels: bad headnode for model %d\n", i );
+ dst->headnode = NULL;
+ } else {
+ dst->headnode = r_world.nodes + headnode;
+ }
+
+ dst->type = MODEL_BSP;
+ dst->firstFace = r_world.surfaces + firstFace;
+ dst->numFaces = numFaces;
+
+ LV( mins );
+ LV( maxs );
+ LV( origin );
+
+ dst->radius = RadiusFromBounds( dst->mins, dst->maxs );
+
+ src++; dst++;
+ }
+
+ return 0;
+}
+
+
+static void Bsp_SetParent( bspNode_t *node ) {
+ bspNode_t *child;
+
+ while( node->plane ) {
+ child = node->children[0];
+ child->parent = node;
+ Bsp_SetParent( child );
+
+ child = node->children[1];
+ child->parent = node;
+ node = child;
+ }
+
+}
+
+void Bsp_FreeWorld( void ) {
+ if( r_world.name[0] ) {
+ sys.HunkFree( &r_world.pool );
+ memset( &r_world, 0, sizeof( r_world ) );
+ }
+}
+
+qboolean Bsp_LoadWorld( const char *path ) {
+ dheader_t header;
+ lump_t *lump;
+ int i;
+ byte *data;
+ int length;
+
+ length = fs.LoadFileEx( path, ( void ** )&data, FS_FLAG_CACHE );
+ if( !data ) {
+ return qfalse;
+ }
+
+ if( length < sizeof( header ) ) {
+ Com_EPrintf( "Bsp_Load: file length < header length\n" );
+ goto fail;
+ }
+
+ /* byte swap and validate the header */
+ header = *( dheader_t * )data;
+ header.ident = LittleLong( header.ident );
+ header.version = LittleLong( header.version );
+ if( header.ident != IDBSPHEADER ) {
+ Com_EPrintf( "Bsp_Load: not an IBSP file: %x != %x\n",
+ header.ident, IDBSPHEADER );
+ goto fail;
+ }
+ if( header.version != BSPVERSION ) {
+ Com_EPrintf( "Bsp_Load: wrong version number: %u != %u\n",
+ header.version, BSPVERSION );
+ goto fail;
+ }
+
+ /* byte swap and validate lumps */
+ lump = header.lumps;
+ for( i = 0; i < HEADER_LUMPS; i++ ) {
+ lump->fileofs = LittleLong( lump->fileofs );
+ lump->filelen = LittleLong( lump->filelen );
+ if( lump->fileofs + lump->filelen > length ) {
+ Com_EPrintf( "Bsp_Load: lump #%d out of bounds\n", i );
+ goto fail;
+ }
+ lump++;
+ }
+
+ loadData = data;
+
+
+ /* reserve 16 MB of virtual memory */
+ sys.HunkBegin( &r_world.pool, 0x1000000 );
+
+#define BSP_LOAD( Func, Lump ) \
+ if( Bsp_Load##Func( &header.lumps[LUMP_##Lump] ) ) { \
+ sys.HunkFree( &r_world.pool ); \
+ goto fail; \
+ }
+
+ BSP_LOAD( Vis, VISIBILITY )
+ BSP_LOAD( Vertices, VERTEXES )
+ BSP_LOAD( Edges, EDGES )
+ BSP_LOAD( SurfEdges, SURFEDGES )
+ BSP_LOAD( Texinfo, TEXINFO )
+ BSP_LOAD( LightMap, LIGHTING )
+ BSP_LOAD( Planes, PLANES )
+ BSP_LOAD( Faces, FACES )
+ BSP_LOAD( LeafFaces, LEAFFACES )
+ BSP_LOAD( Leafs, LEAFS )
+ BSP_LOAD( Nodes, NODES )
+ BSP_LOAD( Submodels, MODELS )
+// BSP2_LOAD( EntityString, ENTITIES )
+
+ fs.FreeFile( data );
+
+ sys.HunkEnd( &r_world.pool );
+
+ Bsp_SetParent( r_world.nodes );
+
+ strcpy( r_world.name, path );
+
+ return qtrue;
+
+fail:
+ fs.FreeFile( data );
+ return qfalse;
+}
+
+