mapzone/map.cpp

Go to the documentation of this file.
00001 /*
00002  * map.cpp
00003  *
00004  * Copyright (C) 2008,2009  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  * Simple implementation of a map object (see mapzone.h)
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "mapzone.h"
00036 
00037 #include <fstream>
00038 
00039 #include "common/wave_ex.h"
00040 #include "typeinst/typeinst.h"
00041 #include "perf/perf.h"
00042 #include "util/file.h"
00043 #include "util/parsing.h"
00044 #include "util/token_stream.h"
00045 
00046 
00047 namespace mapzone {
00048 
00049 
00050 ////////////////////////////////////////////////////////////////////////////////
00051 //
00052 //      static helper methods
00053 //
00054 ////////////////////////////////////////////////////////////////////////////////
00055 
00056 
00057 
00058 ////////////////////////////////////////////////////////////////////////////////
00059 //
00060 //      Reader -- object that implements aesop::MapFormatReader interface
00061 //      (reference implementation)
00062 //
00063 ////////////////////////////////////////////////////////////////////////////////
00064 
00065 class Reader : public aesop::MapFormatReader {
00066 public:
00067         ~Reader(void) throw() { }
00068 
00069         // aesop::MapFormatReader class interface methods ---------------------
00070         const char * getDescription(void);
00071         bool isMyFormat(IN const char * id,
00072                                 IN const char * path);
00073         smart_ptr<aesop::Map> loadMap(IN const char * id,
00074                                 IN const char * path);
00075 
00076 private:
00077 };
00078 
00079 
00080 const char *
00081 Reader::getDescription
00082 (
00083 void
00084 )
00085 {
00086         return "Reference format (mapzone library)";
00087 }
00088 
00089 
00090 
00091 bool
00092 Reader::isMyFormat
00093 (
00094 IN const char * id,
00095 IN const char * path
00096 )
00097 {
00098         ASSERT(id, "null");     // don't really use ID here...
00099         ASSERT(path, "null");
00100 
00101         // verify the extension is ".map"
00102         if (strcmp("map", GetExtension(path))) {
00103                 return false;   // does not end in ".map"
00104         }
00105 
00106         // crack the file, verify the format
00107         std::ifstream infile(path);
00108         if (!infile.good()) {
00109                 WAVE_EX(wex);
00110                 wex << "Failed to open map '" << path << "'.";
00111         }
00112 
00113         std::string token;
00114         getNextToken(infile, token);
00115         if ("mapFormat" != token) {
00116                 return false;
00117         }
00118         getNextToken(infile, token);
00119         if ("mapzone-0.1" != token) {
00120                 return false;
00121         }
00122 
00123         // right extension, and contains proper leading format tags!
00124         return true;
00125 }
00126 
00127 
00128 
00129 smart_ptr<aesop::Map>
00130 Reader::loadMap
00131 (
00132 IN const char * id,
00133 IN const char * path
00134 )
00135 {
00136         ASSERT(id, "null");
00137         ASSERT(path, "null");
00138         ASSERT(this->isMyFormat(id, path),
00139             "Not my format?  path='%s'", path);
00140 
00141         std::ifstream infile(path);
00142         if (!infile.good()) {
00143                 WAVE_EX(wex);
00144                 wex << "Failed to open map '" << path << "'";
00145         }
00146 
00147         // read header (basic!)
00148         expectToken(infile, "mapFormat");
00149         expectToken(infile, "mapzone-0.1");
00150         expectToken(infile, "map");
00151 
00152         // call helper for actual map data
00153         return mapzone::loadMap(id, infile);
00154 }
00155 
00156 
00157 
00158 
00159 ////////////////////////////////////////////////////////////////////////////////
00160 //
00161 //      MapImpl -- object that implements aesop::Map interface
00162 //      (reference implementation)
00163 //
00164 ////////////////////////////////////////////////////////////////////////////////
00165 
00166 class MapImpl : public aesop::Map {
00167 public:
00168         // constructor, destructor ---------------------------------------------
00169         ~MapImpl(void) throw() { }
00170 
00171         // public class methods ------------------------------------------------
00172         void initialize(IN const char * id,
00173                                 IN std::istream& stream);
00174 
00175         // aesop::Map class interface methods ----------------------------------
00176         const char * getId(void) const throw() { return m_id.c_str(); }
00177         aesop::Zone * getRootZone(void) throw();
00178         aesop::Zone * getZone(IN const char * zone_id);
00179         void iterateZones(IN aesop::zone_iteration_fn callback,
00180                                 IN void * context);
00181         const char * getDefaultStartingPointId(void) const throw()
00182                                 { return m_defaultStartId.c_str(); }
00183         void getStartingPointIds(OUT VecString& ids) const;
00184         void getStartingPoint(IN const char * id,
00185                                 OUT aesop::destination_t& start) const;
00186 
00187 private:
00188         // private typedefs ----------------------------------------------------
00189         typedef std::map<std::string, aesop::destination_t> dest_map_t;
00190         
00191         // private helper methods ----------------------------------------------
00192         void readDestination(IO std::istream& stream,
00193                                 OUT aesop::destination_t& dest);
00194 
00195         // private data members ------------------------------------------------
00196         std::string             m_id;           // unique ID
00197         std::string             m_rootZoneId;   // ID of root zone
00198         std::string             m_defaultStartId;// ID of default start point
00199         zone_map_t              m_zones;        // zone ID --> zone
00200         dest_map_t              m_startPoints;  // start point ID --> dest
00201 };
00202 
00203 
00204 
00205 void
00206 MapImpl::initialize
00207 (
00208 IN const char * id,
00209 IN std::istream& stream
00210 )
00211 {
00212         perf::Timer timer("mapzone::Map::initialize");
00213         ASSERT(id, "null");
00214         ASSERT(stream.good(), "bad?");
00215 
00216         // remember ID
00217         m_id = id;
00218         DPRINTF("Map id='%s'", m_id.c_str());
00219 
00220         // start
00221         expectToken(stream, "{");
00222 
00223         // read map metadata
00224         expectToken(stream, "rootZone");
00225         getNextToken(stream, m_rootZoneId);
00226         DPRINTF("Root zone id='%s'", m_rootZoneId.c_str());
00227 
00228         // read default starting point
00229         expectToken(stream, "defaultStart");
00230         getNextToken(stream, m_defaultStartId);
00231         DPRINTF("Default start id='%s'", m_defaultStartId.c_str());
00232 
00233         // first pass--load various objects
00234         for (;;) {
00235                 // have a new zone?
00236                 std::string token;
00237                 getNextToken(stream, token);
00238                 if ("}" == token) {
00239                         break;          // end of map
00240                 }
00241 
00242                 aesop::Zone::eType type = aesop::Zone::eType_Invalid;
00243                 if ("leafZone" == token) {
00244                         type = aesop::Zone::eType_Leaf;
00245                 } else if ("parentZone" == token) {
00246                         type = aesop::Zone::eType_Parent;
00247                 } else if ("virtualLeafZone" == token) {
00248                         type = aesop::Zone::eType_VirtualLeaf;
00249                 } else if ("start" == token) {
00250                         aesop::destination_t d;
00251                         this->readDestination(stream, d);
00252                         m_startPoints[d.id] = d;
00253                         continue;       // skip the zone loading
00254                 } else {
00255                         WAVE_EX(wex);
00256                         wex << "Invalid zone spec: '" << token << "'";
00257                 }
00258                 smart_ptr<aesop::Zone> zone = loadZone(stream, type);
00259                 ASSERT(zone, "Failed to load zone from stream?");
00260 
00261                 const char * zone_id = zone->getId();
00262                 DPRINTF("Just loaded zone '%s'", zone_id);
00263 
00264                 if (m_zones.end() != m_zones.find(zone_id)) {
00265                         WAVE_EX(wex);
00266                         wex << "Multiple zones with same ID?  id='";
00267                         wex << zone_id << "'";
00268                 }
00269                 m_zones[zone_id] = zone;
00270                 ASSERT(2 == zone.get_ref_count(), "bad ref count?");
00271         }
00272         DPRINTF("Loaded %d zones", (int) m_zones.size());
00273 
00274         // verify root zone exists
00275         if (m_zones.end() == m_zones.find(m_rootZoneId)) {
00276                 WAVE_EX(wex);
00277                 wex << "Root zone id='" << m_rootZoneId << "', but no such ";
00278                 wex << "zone exists!";
00279         }
00280 
00281         // second pass--let zones resolve IDs
00282         for (zone_map_t::iterator i = m_zones.begin(); i != m_zones.end();
00283              ++i) {
00284                 aesop::Zone * z = i->second;
00285                 ASSERT(z, "null zone in map?");
00286 
00287                 resolveZoneIds(z, m_zones);
00288         }
00289 
00290         // third pass--have zones calculate bounding rects
00291         for (zone_map_t::iterator i = m_zones.begin(); i != m_zones.end();
00292              ++i) {
00293                 aesop::Zone * z = i->second;
00294                 ASSERT(z, "null zone in map?");
00295 
00296                 calcBoundaries(z);
00297 
00298                 // comment this out eventually!  Just for debugging...
00299                 rect3d_t r;
00300                 z->getBoundingRect(r);
00301                 r.dump(z->getId());
00302         }
00303 }
00304 
00305 
00306 
00307 aesop::Zone *
00308 MapImpl::getRootZone
00309 (
00310 void
00311 )
00312 throw()
00313 {
00314         return this->getZone(m_rootZoneId.c_str());
00315 }
00316 
00317 
00318 
00319 aesop::Zone *
00320 MapImpl::getZone
00321 (
00322 IN const char * zone_id
00323 )
00324 {
00325         ASSERT(zone_id, "null");
00326 
00327         zone_map_t::iterator i = m_zones.find(zone_id);
00328         if (m_zones.end() == i) {
00329                 WAVE_EX(wex);
00330                 wex << "Failed to find zone with id='" << zone_id << "'";
00331         }
00332 
00333         return i->second;
00334 }
00335 
00336 
00337 
00338 void
00339 MapImpl::iterateZones
00340 (
00341 IN aesop::zone_iteration_fn callback,
00342 IN void * context
00343 )
00344 {
00345         ASSERT(callback, "null");
00346         // ASSERT(context) -- can be null
00347 
00348         for (zone_map_t::iterator i = m_zones.begin(); i != m_zones.end();
00349              ++i) {
00350                 aesop::Zone * z = i->second;
00351                 ASSERT(z, "null zone in map");
00352 
00353                 // tell caller
00354                 callback(z, context);
00355         }
00356 }
00357 
00358 
00359 
00360 void
00361 MapImpl::getStartingPointIds
00362 (
00363 OUT VecString& ids
00364 )
00365 const
00366 {
00367         ids.clear();
00368 
00369         for (dest_map_t::const_iterator i = m_startPoints.begin();
00370              i != m_startPoints.end(); ++i) {
00371                 ids.push_back(i->first);
00372         }
00373 }
00374 
00375 
00376 
00377 void
00378 MapImpl::getStartingPoint
00379 (
00380 IN const char * id,
00381 OUT aesop::destination_t& start
00382 )
00383 const
00384 {
00385         ASSERT(id, "null");
00386 
00387         dest_map_t::const_iterator i = m_startPoints.find(id);
00388         if (m_startPoints.end() == i) {
00389                 WAVE_EX(wex);
00390                 wex << "Invalid start point: "  << id;
00391         }
00392 
00393         start = i->second;
00394 }
00395 
00396 
00397 
00398 ////////////////////////////////////////////////////////////////////////////////
00399 //
00400 //      MapImpl -- private helper methods
00401 //
00402 ////////////////////////////////////////////////////////////////////////////////
00403 
00404 void
00405 MapImpl::readDestination
00406 (
00407 IO std::istream& stream,
00408 OUT aesop::destination_t& dest
00409 )
00410 {
00411         ASSERT(stream.good(), "bad?");
00412         dest.clear();
00413 
00414         std::string token;
00415 
00416         // open bracket, read ID
00417         expectToken(stream, "{");
00418         expectToken(stream, "id");
00419         getNextToken(stream, token);
00420         if (token.length() > aesop::eAESOP_MaxIdLength) {
00421                 WAVE_EX(wex);
00422                 wex << "Invalid id: " << token;
00423         }
00424         strcpy(dest.id, token.c_str());
00425 
00426         // read 3D rect of start point
00427         expectToken(stream, "rect");
00428         token = getNextLineFromStream(stream, eParse_Strip);
00429         parseRect3d(token.c_str(), dest.rect);
00430 
00431         // end of struct
00432         expectToken(stream, "}");
00433 }
00434 
00435 
00436 
00437 ////////////////////////////////////////////////////////////////////////////////
00438 //
00439 //      public API
00440 //
00441 ////////////////////////////////////////////////////////////////////////////////
00442 
00443 smart_ptr<aesop::Map>
00444 loadMap
00445 (
00446 IN const char * id,
00447 IN std::istream& stream
00448 )
00449 {
00450         ASSERT(id, "null");
00451         ASSERT(stream.good(), "not good?");
00452 
00453         smart_ptr<MapImpl> local = new MapImpl;
00454         ASSERT(local, "out of memory");
00455 
00456         local->initialize(id, stream);
00457 
00458         return local;
00459 }
00460 
00461 
00462 
00463 smart_ptr<aesop::MapFormatReader>
00464 getMapFormatReader
00465 (
00466 void
00467 )
00468 {
00469         smart_ptr<Reader> local = new Reader;
00470         ASSERT(local, "out of memory");
00471 
00472         return local;
00473 }
00474 
00475 
00476 
00477 };      // mapzone interface
00478