bsp-version.cpp

Go to the documentation of this file.
00001 /*
00002  * bsp-version.cpp
00003  *
00004  * Copyright (C) 2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "bsp-version.h"                // always include our own header first!
00035 
00036 #include <iostream>
00037 
00038 #include "perf/perf.h"
00039 
00040 
00041 namespace quake {
00042 
00043 
00044 struct lump_meta_t {
00045         eLumpType               type;
00046         const char *            name;
00047 };
00048 
00049 
00050 #define LUMP_META( type ) { eLump_ ## type , #type },
00051 
00052 static const lump_meta_t s_lumpMap[] = {
00053         LUMP_META(Entities)
00054         LUMP_META(Textures)
00055         LUMP_META(Planes)
00056         LUMP_META(Nodes)
00057         LUMP_META(Leafs)
00058         LUMP_META(Leaffaces)
00059         LUMP_META(Leafbrushes)
00060         LUMP_META(Models)
00061         LUMP_META(Brushes)
00062         LUMP_META(Brushsides)
00063         LUMP_META(Vertices)
00064         LUMP_META(Meshverts)
00065         LUMP_META(Effects)
00066         LUMP_META(Faces)
00067         LUMP_META(Lightmaps)
00068         LUMP_META(Lightvols)
00069         LUMP_META(Visdata)
00070 
00071         // keep this last!
00072         { eLump_Invalid,        "Invalid" }
00073 };
00074 
00075 
00076 enum eEndian {
00077         eLittleEndian           = 1,
00078         eBigEndian              = 2,
00079 
00080         eEndian_Invalid         = 0
00081 };
00082 
00083 
00084 
00085 ////////////////////////////////////////////////////////////////////////////////
00086 //
00087 //      static helper methods
00088 //
00089 ////////////////////////////////////////////////////////////////////////////////
00090 
00091 void
00092 littleEndianToHostEndian
00093 (
00094 IO byte_t * raw         ///< points to 4 bytes of little-endian data
00095 )
00096 {
00097         ASSERT(raw, "null");
00098 
00099         // are we big or little endian?
00100         // TODO: is there a better way to do this?  Very hacky...
00101         static eEndian endian = eEndian_Invalid;
00102         if (eEndian_Invalid == endian) {
00103                 DPRINTF("determining local host byte order...");
00104                 short test = 256;
00105                 byte_t b[2];
00106                 short * p = (short *) b;
00107                 *p = test;
00108                 if (b[0]) {
00109                         DPRINTF("Big endian!");
00110                         endian = eBigEndian;
00111                 } else if (b[1]) {
00112                         DPRINTF("Little endian!");
00113                         endian = eLittleEndian;
00114                 }
00115         }
00116         ASSERT(endian, "should have determined endian-ness");
00117 
00118         // already little-endian?  No need to convert
00119         if (eLittleEndian == endian) {
00120                 return;
00121         }
00122 
00123         // flip
00124         byte_t save0 = raw[0];
00125         byte_t save1 = raw[1];
00126         raw[0] = raw[3];
00127         raw[1] = raw[2];
00128         raw[2] = save1;
00129         raw[3] = save0;
00130 }
00131 
00132 
00133 
00134 static eBspVendor
00135 parseVendor
00136 (
00137 IO std::istream& in
00138 )
00139 {
00140         ASSERT_THROW(in.good(), "bad input BSP stream");
00141 
00142         char buffer[5];
00143         in.read(buffer, 4);
00144         buffer[4] = 0;
00145 
00146         if (!strcmp("IBSP", buffer)) {
00147                 return eBspVendor_Id;
00148         } else if (!strcmp("VBSP", buffer)) {
00149                 return eBspVendor_Valve;
00150         }
00151 
00152         // got here?  No idea!
00153         ASSERT_THROW(false, "Unknown BSP vendor: '" << buffer << "'");
00154         return eBspVendor_Invalid;
00155 }
00156 
00157 
00158 
00159 static eBspVersion
00160 parseVersion
00161 (
00162 IN eBspVendor vendor,
00163 IN int val
00164 )
00165 {
00166         switch (vendor) {
00167         case eBspVendor_Id:
00168                 {
00169                         switch (val) {
00170                         case 0x2e:
00171                                 return eBspVersion_IdQuake3;
00172                         default:
00173                                 ASSERT_THROW(false,
00174                                     "Unrecognized Id version: " << val);
00175                         }
00176                 }
00177                 break;
00178 
00179         case eBspVendor_Valve:
00180                 {
00181                         switch (val) {
00182                         default:
00183                                 ASSERT_THROW(false,
00184                                     "Unrecognized Valve version: " << val);
00185                         }
00186                 }
00187                 break;
00188 
00189         default:
00190                 ASSERT_THROW(false,
00191                     "Unknown or invalid BSP vendor: " << vendor);
00192         }
00193 
00194         // should never get here
00195         ASSERT(false, "should never get here");
00196         return eBspVersion_Invalid;
00197 }
00198 
00199 
00200 
00201 ////////////////////////////////////////////////////////////////////////////////
00202 //
00203 //      BspVersion -- public class methods
00204 //
00205 ////////////////////////////////////////////////////////////////////////////////
00206 
00207 void
00208 BspVersion::clear
00209 (
00210 void
00211 )
00212 throw()
00213 {
00214         m_vendor = eBspVendor_Invalid;
00215         m_version = eBspVersion_Invalid;
00216 }
00217 
00218 
00219 
00220 bool
00221 BspVersion::isValid
00222 (
00223 void
00224 )
00225 const
00226 throw()
00227 {
00228         return (eBspVendor_Invalid != m_vendor &&
00229                 eBspVersion_Invalid != m_version);
00230 }
00231 
00232 
00233 
00234 void
00235 BspVersion::read
00236 (
00237 IO std::istream& in
00238 )
00239 {
00240         ASSERT_THROW(in.good(), "bad input BSP stream");
00241 
00242         m_vendor = parseVendor(in);
00243         m_version = parseVersion(m_vendor, readInt(in));
00244 }
00245 
00246 
00247 
00248 #define ADDLUMP( type ) lumps.push_back( eLump_ ## type );
00249 
00250 bool
00251 BspVersion::getLumpTypes
00252 (
00253 OUT vec_lump_type_t& lumps
00254 )
00255 const
00256 {
00257         lumps.clear();
00258 
00259         switch (m_version) {
00260         case eBspVersion_IdQuake3:
00261                 ADDLUMP(Entities)
00262                 ADDLUMP(Textures)
00263                 ADDLUMP(Planes)
00264                 ADDLUMP(Nodes)
00265                 ADDLUMP(Leafs)
00266                 ADDLUMP(Leaffaces)
00267                 ADDLUMP(Leafbrushes)
00268                 ADDLUMP(Models)
00269                 ADDLUMP(Brushes)
00270                 ADDLUMP(Brushsides)
00271                 ADDLUMP(Vertices)
00272                 ADDLUMP(Meshverts)
00273                 ADDLUMP(Effects)
00274                 ADDLUMP(Faces)
00275                 ADDLUMP(Lightmaps)
00276                 ADDLUMP(Lightvols)
00277                 ADDLUMP(Visdata)
00278                 return true;
00279 
00280         default:
00281                 break;
00282         }
00283 
00284         // got here?  Can't determine lump types
00285         DPRINTF("Can't determine lumps from bsp vendor + version");
00286         return false;
00287 }
00288 
00289 
00290 
00291 ////////////////////////////////////////////////////////////////////////////////
00292 //
00293 //      public API
00294 //
00295 ////////////////////////////////////////////////////////////////////////////////
00296 
00297 const char *
00298 getLumpName
00299 (
00300 IN eLumpType type
00301 )
00302 {
00303         for (const lump_meta_t * p = s_lumpMap; eLump_Invalid != p->type; ++p)
00304         {
00305                 if (p->type == type) {
00306                         return p->name;
00307                 }
00308         }
00309 
00310         DPRINTF("Unknown lump type: %d", type);
00311         return "(unknown)";
00312 }
00313 
00314 
00315 
00316 int
00317 readInt
00318 (
00319 IO std::istream& in
00320 )
00321 {
00322         ASSERT_THROW(in.good(), "bad BSP input stream");
00323 
00324         // read the integer in little-endian
00325         byte_t raw[4];
00326         in.read((char *) raw, 4);
00327 
00328         // convert
00329         littleEndianToHostEndian(raw);
00330 
00331         // recast bytes as integer
00332         int32_t * pi = (int32_t *) raw;
00333         return *pi;
00334 }
00335 
00336 
00337 
00338 float
00339 readFloat
00340 (
00341 IO std::istream& in
00342 )
00343 {
00344         ASSERT_THROW(in.good(), "bad BSP input stream");
00345 
00346         // read the float in little-endian
00347         byte_t raw[4];
00348         in.read((char *) raw, 4);
00349 
00350         // convert
00351         littleEndianToHostEndian(raw);
00352 
00353         // recast bytes as float
00354         float * pf = (float *) raw;
00355         return *pf;
00356 }
00357 
00358 
00359 
00360 point3d_t
00361 readPoint3d
00362 (
00363 IO std::istream& in
00364 )
00365 {
00366         point3d_t p;
00367         p.x = readFloat(in);
00368         p.z = readFloat(in);
00369         p.y = readFloat(in);
00370 
00371         return p;
00372 }
00373 
00374 
00375 
00376 };      // quake namespace
00377