/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU bteral Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code 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 bteral Public License for more details. You should have received a copy of the GNU bteral Public License along with Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "BspLoader.h" #include #include typedef struct { char filename[1024]; char *buffer,*script_p,*end_p; int line; } BSPScript; #define MAX_INCLUDES 8 BSPScript scriptstack[MAX_INCLUDES]; BSPScript *script; int scriptline; char token[BSPMAXTOKEN]; bool endofscript; bool tokenready; // only true if UnGetToken was just called // //loadBSPFile // int extrasize = 100; BspLoader::BspLoader() :m_num_entities(0) { m_Endianness = getMachineEndianness(); if (m_Endianness == BSP_BIG_ENDIAN) { printf("Machine is BIG_ENDIAN\n"); } else { printf("Machine is Little Endian\n"); } } bool BspLoader::loadBSPFile( void* memoryBuffer) { BSPHeader *header = (BSPHeader*) memoryBuffer; // load the file header if (header) { // swap the header swapBlock( (int *)header, sizeof(*header) ); int length = (header->lumps[BSPLUMP_SHADERS].filelen) / sizeof(BSPShader); m_dshaders.resize(length+extrasize); m_numShaders = copyLump( header, BSPLUMP_SHADERS, &m_dshaders[0], sizeof(BSPShader) ); length = (header->lumps[LUMP_MODELS].filelen) / sizeof(BSPModel); m_dmodels.resize(length+extrasize); m_nummodels = copyLump( header, LUMP_MODELS, &m_dmodels[0], sizeof(BSPModel) ); length = (header->lumps[BSPLUMP_PLANES].filelen) / sizeof(BSPPlane); m_dplanes.resize(length+extrasize); m_numplanes = copyLump( header, BSPLUMP_PLANES, &m_dplanes[0], sizeof(BSPPlane) ); length = (header->lumps[BSPLUMP_LEAFS].filelen) / sizeof(BSPLeaf); m_dleafs.resize(length+extrasize); m_numleafs = copyLump( header, BSPLUMP_LEAFS, &m_dleafs[0], sizeof(BSPLeaf) ); length = (header->lumps[BSPLUMP_NODES].filelen) / sizeof(BSPNode); m_dnodes.resize(length+extrasize); m_numnodes = copyLump( header, BSPLUMP_NODES, &m_dnodes[0], sizeof(BSPNode) ); length = (header->lumps[BSPLUMP_LEAFSURFACES].filelen) / sizeof(m_dleafsurfaces[0]); m_dleafsurfaces.resize(length+extrasize); m_numleafsurfaces = copyLump( header, BSPLUMP_LEAFSURFACES, &m_dleafsurfaces[0], sizeof(m_dleafsurfaces[0]) ); length = (header->lumps[BSPLUMP_LEAFBRUSHES].filelen) / sizeof(m_dleafbrushes[0]) ; m_dleafbrushes.resize(length+extrasize); m_numleafbrushes = copyLump( header, BSPLUMP_LEAFBRUSHES, &m_dleafbrushes[0], sizeof(m_dleafbrushes[0]) ); length = (header->lumps[LUMP_BRUSHES].filelen) / sizeof(BSPBrush); m_dbrushes.resize(length+extrasize); m_numbrushes = copyLump( header, LUMP_BRUSHES, &m_dbrushes[0], sizeof(BSPBrush) ); length = (header->lumps[LUMP_BRUSHSIDES].filelen) / sizeof(BSPBrushSide); m_dbrushsides.resize(length+extrasize); m_numbrushsides = copyLump( header, LUMP_BRUSHSIDES, &m_dbrushsides[0], sizeof(BSPBrushSide) ); length = (header->lumps[LUMP_SURFACES].filelen) / sizeof(BSPSurface); m_drawSurfaces.resize(length+extrasize); m_numDrawSurfaces = copyLump( header, LUMP_SURFACES, &m_drawSurfaces[0], sizeof(BSPSurface) ); length = (header->lumps[LUMP_DRAWINDEXES].filelen) / sizeof(m_drawIndexes[0]); m_drawIndexes.resize(length+extrasize); m_numDrawIndexes = copyLump( header, LUMP_DRAWINDEXES, &m_drawIndexes[0], sizeof(m_drawIndexes[0]) ); length = (header->lumps[LUMP_VISIBILITY].filelen) / 1; m_visBytes.resize(length+extrasize); m_numVisBytes = copyLump( header, LUMP_VISIBILITY, &m_visBytes[0], 1 ); length = (header->lumps[LUMP_LIGHTMAPS].filelen) / 1; m_lightBytes.resize(length+extrasize); m_numLightBytes = copyLump( header, LUMP_LIGHTMAPS, &m_lightBytes[0], 1 ); length = (header->lumps[BSPLUMP_ENTITIES].filelen) / 1; m_dentdata.resize(length+extrasize); m_entdatasize = copyLump( header, BSPLUMP_ENTITIES, &m_dentdata[0], 1); length = (header->lumps[LUMP_LIGHTGRID].filelen) / 1; m_gridData.resize(length+extrasize); m_numGridPoints = copyLump( header, LUMP_LIGHTGRID, &m_gridData[0], 8 ); // swap everything swapBSPFile(); return true; } return false; } const char* BspLoader::getValueForKey( const BSPEntity* ent, const char* key ) const { const BSPKeyValuePair* ep; for (ep=ent->epairs ; ep ; ep=ep->next) { if (!strcmp(ep->key, key) ) { return ep->value; } } return ""; } float BspLoader::getFloatForKey( const BSPEntity *ent, const char *key ) { const char *k; k = getValueForKey( ent, key ); return float(atof(k)); } bool BspLoader::getVectorForKey( const BSPEntity *ent, const char *key, BSPVector3 vec ) { const char *k; k = getValueForKey (ent, key); if (strcmp(k, "")) { sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]); return true; } return false; } /* ============== parseFromMemory ============== */ void BspLoader::parseFromMemory (char *buffer, int size) { script = scriptstack; script++; if (script == &scriptstack[MAX_INCLUDES]) { //printf("script file exceeded MAX_INCLUDES"); } strcpy (script->filename, "memory buffer" ); script->buffer = buffer; script->line = 1; script->script_p = script->buffer; script->end_p = script->buffer + size; endofscript = false; tokenready = false; } bool BspLoader::isEndOfScript (bool crossline) { if (!crossline) //printf("Line %i is incomplete\n",scriptline); if (!strcmp (script->filename, "memory buffer")) { endofscript = true; return false; } //free (script->buffer); if (script == scriptstack+1) { endofscript = true; return false; } script--; scriptline = script->line; //printf ("returning to %s\n", script->filename); return getToken (crossline); } /* ============== getToken ============== */ bool BspLoader::getToken (bool crossline) { char *token_p; if (tokenready) // is a token allready waiting? { tokenready = false; return true; } if (script->script_p >= script->end_p) return isEndOfScript (crossline); // // skip space // skipspace: while (*script->script_p <= 32) { if (script->script_p >= script->end_p) return isEndOfScript (crossline); if (*script->script_p++ == '\n') { if (!crossline) { //printf("Line %i is incomplete\n",scriptline); } scriptline = script->line++; } } if (script->script_p >= script->end_p) return isEndOfScript (crossline); // ; # // comments if (*script->script_p == ';' || *script->script_p == '#' || ( script->script_p[0] == '/' && script->script_p[1] == '/') ) { if (!crossline) { //printf("Line %i is incomplete\n",scriptline); } while (*script->script_p++ != '\n') if (script->script_p >= script->end_p) return isEndOfScript (crossline); scriptline = script->line++; goto skipspace; } // /* */ comments if (script->script_p[0] == '/' && script->script_p[1] == '*') { if (!crossline) { //printf("Line %i is incomplete\n",scriptline); } script->script_p+=2; while (script->script_p[0] != '*' && script->script_p[1] != '/') { if ( *script->script_p == '\n' ) { scriptline = script->line++; } script->script_p++; if (script->script_p >= script->end_p) return isEndOfScript (crossline); } script->script_p += 2; goto skipspace; } // // copy token // token_p = token; if (*script->script_p == '"') { // quoted token script->script_p++; while (*script->script_p != '"') { *token_p++ = *script->script_p++; if (script->script_p == script->end_p) break; if (token_p == &token[BSPMAXTOKEN]) { //printf ("Token too large on line %i\n",scriptline); } } script->script_p++; } else // regular token while ( *script->script_p > 32 && *script->script_p != ';') { *token_p++ = *script->script_p++; if (script->script_p == script->end_p) break; if (token_p == &token[BSPMAXTOKEN]) { //printf ("Token too large on line %i\n",scriptline); } } *token_p = 0; if (!strcmp (token, "$include")) { //getToken (false); //AddScriptToStack (token); return false;//getToken (crossline); } return true; } char *BspLoader::copystring(const char *s) { char *b; b = (char*) malloc( strlen(s)+1); strcpy (b, s); return b; } void BspLoader::stripTrailing( char *e ) { char *s; s = e + strlen(e)-1; while (s >= e && *s <= 32) { *s = 0; s--; } } /* ================= parseEpair ================= */ BSPKeyValuePair *BspLoader::parseEpair( void ) { BSPKeyValuePair *e; e = (struct BSPPair*) malloc( sizeof(BSPKeyValuePair)); memset( e, 0, sizeof(BSPKeyValuePair) ); if ( strlen(token) >= BSPMAX_KEY-1 ) { //printf ("ParseEpar: token too long"); } e->key = copystring( token ); getToken( false ); if ( strlen(token) >= BSPMAX_VALUE-1 ) { //printf ("ParseEpar: token too long"); } e->value = copystring( token ); // strip trailing spaces that sometimes get accidentally // added in the editor stripTrailing( e->key ); stripTrailing( e->value ); return e; } /* ================ parseEntity ================ */ bool BspLoader::parseEntity( void ) { BSPKeyValuePair *e; BSPEntity *mapent; if ( !getToken (true) ) { return false; } if ( strcmp (token, "{") ) { //printf ("parseEntity: { not found"); } BSPEntity bla; bla.brushes = 0; bla.epairs = 0; bla.firstDrawSurf = 0; bla.origin[0] = 0.f; bla.origin[1] = 0.f; bla.origin[2] = 0.f; bla.patches = 0; m_entities.push_back(bla); mapent = &m_entities[m_entities.size()-1]; m_num_entities++; do { if ( !getToken (true) ) { //printf("parseEntity: EOF without closing brace"); } if ( !strcmp (token, "}") ) { break; } e = (struct BSPPair*)parseEpair (); e->next = mapent->epairs; mapent->epairs = e; } while (1); return true; } /* ================ parseEntities Parses the dentdata string into entities ================ */ void BspLoader::parseEntities( void ) { m_num_entities = 0; m_entities.clear(); parseFromMemory( &m_dentdata[0], m_entdatasize ); while ( parseEntity () ) { } } int BspLoader::getMachineEndianness() { long int i = 1; const char *p = (const char *) &i; if (p[0] == 1) // Lowest address contains the least significant byte return BSP_LITTLE_ENDIAN; else return BSP_BIG_ENDIAN; } short BspLoader::isLittleShort (short l) { if (machineEndianness() == BSP_BIG_ENDIAN) { unsigned char b1,b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } //little endian return l; } short BspLoader::isBigShort (short l) { if (machineEndianness() == BSP_BIG_ENDIAN) { return l; } unsigned char b1,b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } int BspLoader::isLittleLong (int l) { if (machineEndianness() == BSP_BIG_ENDIAN) { unsigned char b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } //little endian return l; } int BspLoader::isBigLong (int l) { if (machineEndianness() == BSP_BIG_ENDIAN) { return l; } unsigned char b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } float BspLoader::isLittleFloat (float l) { if (machineEndianness() == BSP_BIG_ENDIAN) { union {unsigned char b[4]; float f;} in, out; in.f = l; out.b[0] = in.b[3]; out.b[1] = in.b[2]; out.b[2] = in.b[1]; out.b[3] = in.b[0]; return out.f; } //little endian return l; } float BspLoader::isBigFloat (float l) { if (machineEndianness() == BSP_BIG_ENDIAN) { return l; } //little endian union {unsigned char b[4]; float f;} in, out; in.f = l; out.b[0] = in.b[3]; out.b[1] = in.b[2]; out.b[2] = in.b[1]; out.b[3] = in.b[0]; return out.f; } // // swapBlock // If all values are 32 bits, this can be used to swap everything // void BspLoader::swapBlock( int *block, int sizeOfBlock ) { int i; sizeOfBlock >>= 2; for ( i = 0 ; i < sizeOfBlock ; i++ ) { block[i] = isLittleLong( block[i] ); } } // // copyLump // int BspLoader::copyLump( BSPHeader *header, int lump, void *dest, int size ) { int length, ofs; length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; //if ( length % size ) { // printf ("loadBSPFile: odd lump size"); //} memcpy( dest, (unsigned char *)header + ofs, length ); return length / size; } // // swapBSPFile // void BspLoader::swapBSPFile( void ) { int i; // models swapBlock( (int *) &m_dmodels[0], m_nummodels * sizeof( m_dmodels[0] ) ); // shaders (don't swap the name) for ( i = 0 ; i < m_numShaders ; i++ ) { m_dshaders[i].contentFlags = isLittleLong( m_dshaders[i].contentFlags ); m_dshaders[i].surfaceFlags = isLittleLong( m_dshaders[i].surfaceFlags ); } // planes swapBlock( (int *)&m_dplanes[0], m_numplanes * sizeof( m_dplanes[0] ) ); // nodes swapBlock( (int *)&m_dnodes[0], m_numnodes * sizeof( m_dnodes[0] ) ); // leafs swapBlock( (int *)&m_dleafs[0], m_numleafs * sizeof( m_dleafs[0] ) ); // leaffaces swapBlock( (int *)&m_dleafsurfaces[0], m_numleafsurfaces * sizeof( m_dleafsurfaces[0] ) ); // leafbrushes swapBlock( (int *)&m_dleafbrushes[0], m_numleafbrushes * sizeof( m_dleafbrushes[0] ) ); // brushes swapBlock( (int *)&m_dbrushes[0], m_numbrushes * sizeof( m_dbrushes[0] ) ); // brushsides swapBlock( (int *)&m_dbrushsides[0], m_numbrushsides * sizeof( m_dbrushsides[0] ) ); // vis ((int *)&m_visBytes)[0] = isLittleLong( ((int *)&m_visBytes)[0] ); ((int *)&m_visBytes)[1] = isLittleLong( ((int *)&m_visBytes)[1] ); // drawindexes swapBlock( (int *)&m_drawIndexes[0], m_numDrawIndexes * sizeof( m_drawIndexes[0] ) ); // drawsurfs swapBlock( (int *)&m_drawSurfaces[0], m_numDrawSurfaces * sizeof( m_drawSurfaces[0] ) ); } bool BspLoader::findVectorByName(float* outvec,const char* name) { const char *cl; BSPVector3 origin; bool found = false; parseEntities(); for ( int i = 1; i < m_num_entities; i++ ) { cl = getValueForKey (&m_entities[i], "classname"); if ( !strcmp( cl, "info_player_start" ) ) { getVectorForKey( &m_entities[i], "origin", origin ); found = true; break; } if ( !strcmp( cl, "info_player_deathmatch" ) ) { getVectorForKey( &m_entities[i], "origin", origin ); found = true; break; } } if (found) { outvec[0] = origin[0]; outvec[1] = origin[1]; outvec[2] = origin[2]; } return found; } const BSPEntity * BspLoader::getEntityByValue( const char* name, const char* value) { const BSPEntity* entity = NULL; for ( int i = 1; i < m_num_entities; i++ ) { const BSPEntity& ent = m_entities[i]; const char* cl = getValueForKey (&m_entities[i], name); if ( !strcmp( cl, value ) ) { entity = &ent; break; } } return entity; }