story.cpp

Go to the documentation of this file.
00001 /*
00002  * story.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  * Implementation of basic story object.  See story.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "story.h"              // always include our own header first!
00036 
00037 #include <iostream>
00038 #include <fstream>
00039 
00040 #include "common/wave_ex.h"
00041 #include "perf/perf.h"
00042 #include "util/token_stream.h"
00043 
00044 
00045 namespace story {
00046 
00047 
00048 static const char * s_storyFilename             = "story.data";
00049 
00050 
00051 // interface destructor implementation
00052 Story::~Story(void) throw() { }
00053 
00054 
00056 //
00057 //      static helper methods
00058 //
00060 
00061 class StoryImpl : public Story {
00062 public:
00063         // constructor, destructor ---------------------------------------------
00064         ~StoryImpl(void) throw() { }
00065 
00066         // public class methods ------------------------------------------------
00067         void initialize(IN const char * locale,
00068                                 IN const char * story_dir,
00069                                 IN std::istream& stream);
00070 
00071         // story::Story class interface methods --------------------------------
00072         const char * getUuid(void) const throw() { return m_uuid.c_str(); }
00073         const char * getTitle(void) const throw() { return m_title.c_str(); }
00074         const char * getStartingMapId(void) const throw() {
00075                                 return m_startingMapId.c_str(); }
00076         std::string getObjectPath(IN const char * objectType,
00077                                 IN const char * objectId) const;
00078         smart_ptr<i18n::Manager> getI18nManager(IN const char * path);
00079 
00080 private:
00081 
00082         // private data members ------------------------------------------------
00083         std::string             m_uuid;
00084         std::string             m_title;
00085         std::string             m_startingMapId;
00086         std::string             m_storyDir;     // root directory
00087         std::string             m_locale;
00088         smart_ptr<nstream::Manager> m_nstreamMgr; // filesystem manager
00089 };
00090 
00091 
00092 
00093 void
00094 StoryImpl::initialize
00095 (
00096 IN const char * locale,
00097 IN const char * story_dir,
00098 IN std::istream& stream
00099 )
00100 {
00101         perf::Timer timer("story::initialize");
00102         ASSERT(locale, "null");
00103         ASSERT(story_dir, "null");
00104         ASSERT(stream.good(), "bad?");
00105 
00106         m_locale = locale;
00107 
00108         // remember where we are
00109         m_storyDir = story_dir;
00110 
00111         // create named stream manager
00112         // TODO: this should be passed in!  Assuming local filesystem for now.
00113         m_nstreamMgr = nstream::getFilesystemManager(story_dir);
00114         ASSERT_THROW(m_nstreamMgr,
00115             "Failed to create a named stream manager for local directory: " <<
00116             story_dir);
00117 
00118         // does the directory already end with a slash?
00119         int len = m_storyDir.length();
00120         if (len < 1) {
00121                 WAVE_EX(wex);
00122                 wex << "Invalid story directory: " << story_dir;
00123         }
00124         if ('/' != story_dir[len - 1]) {
00125                 // append a trailing slash
00126                 m_storyDir += "/";
00127         }
00128 
00129         // start
00130         expectToken(stream, "{");
00131 
00132         // parse stream
00133         expectToken(stream, "uuid");
00134         getNextToken(stream, m_uuid);
00135         DPRINTF("Loading story with uuid='%s'", m_uuid.c_str());
00136 
00137         expectToken(stream, "title");
00138         getNextToken(stream, m_title);
00139         DPRINTF("title = '%s'", m_title.c_str());
00140 
00141         expectToken(stream, "startingMapId");
00142         getNextToken(stream, m_startingMapId);
00143         DPRINTF("Starting map has id='%s'", m_startingMapId.c_str());
00144 
00145         // that's it
00146         expectToken(stream, "}");
00147 }
00148 
00149 
00150 
00152 //
00153 //      StoryImpl -- story::Story class interface methods
00154 //
00156 
00157 std::string
00158 StoryImpl::getObjectPath
00159 (
00160 IN const char * objectType,
00161 IN const char * objectId
00162 )
00163 const
00164 {
00165         ASSERT(objectType, "null");
00166         ASSERT(objectId, "null");
00167 
00168         // construct full pathname
00169         std::string full_path = m_storyDir;     // definitely ends with '/'
00170         full_path += objectType;
00171         full_path += "/";
00172         full_path += objectId;
00173 
00174         return full_path;
00175 }
00176 
00177 
00178 
00179 smart_ptr<i18n::Manager>
00180 StoryImpl::getI18nManager
00181 (
00182 IN const char * path
00183 )
00184 {
00185         ASSERT(path, "null");
00186         ASSERT(m_nstreamMgr, "null");
00187 
00188         // construct full relative path
00189         std::string relpath = "strings/";
00190         relpath += m_locale;
00191         relpath += "/";
00192         relpath += path;
00193 
00194         // get a named stream to the appropriate file
00195         smart_ptr<nstream::Stream> stream =
00196             nstream::openNamedStream(m_nstreamMgr, relpath.c_str());
00197         ASSERT(stream, "null");         // above call should throw on error
00198 
00199         smart_ptr<i18n::Manager> mgr = i18n::Manager::create(m_locale.c_str());
00200         ASSERT(mgr, "null");
00201 
00202         mgr->parseStrings(stream);
00203 
00204         return mgr;
00205 }
00206 
00207 
00208 
00210 //
00211 //      public API
00212 //
00214 
00215 smart_ptr<Story>
00216 Story::create
00217 (
00218 IN const char * locale,
00219 IN const char * story_dir
00220 )
00221 {
00222         ASSERT(locale, "null");
00223         ASSERT(story_dir, "null");
00224 
00225         // construct filename, open story file
00226         std::string filename = story_dir;
00227         filename += "/";
00228         filename += s_storyFilename;
00229 
00230         DPRINTF("Attempting to load story from '%s'...", filename.c_str());
00231         std::ifstream stream(filename.c_str());
00232         if (!stream.good()) {
00233                 WAVE_EX(wex);
00234                 wex << "Failed to load story from file '" << filename << "'.";
00235                 wex << "  Bad directory '" << story_dir << "'?";
00236         }
00237         expectToken(stream, "story");
00238 
00239         // create story object from file
00240         smart_ptr<StoryImpl> local = new StoryImpl;
00241         ASSERT(local, "out of memory");
00242         local->initialize(locale, story_dir, stream);
00243 
00244         return local;
00245 }
00246 
00247 
00248 
00249 };      // story namespace
00250