gamepad-type.cpp

Go to the documentation of this file.
00001 /*
00002  * gamepad-type.cpp
00003  *
00004  * Copyright (C) 2009,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  * Simple data-driven gamepad type objects.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "gamepad-type.h"               // always include our own header first
00036 
00037 #include "common/wave_ex.h"
00038 #include "datahash/datahash_util.h"
00039 #include "datahash/datahash_text.h"
00040 #include "perf/perf.h"
00041 #include "util/file.h"
00042 
00043 
00044 namespace gamepad {
00045 
00046 
00047 // interface destructors
00048 Type::~Type(void) throw() { }
00049 
00050 
00051 const char * s_extension                = "gamepad";
00052 
00053 
00054 struct input_entry_t {
00055         eInputType      type;
00056         const char *    name;   // for debugging etc
00057         const char *    key;    // key name as appears in type input files
00058 };
00059 
00060 
00061 static const input_entry_t s_inputTypes[] = {
00062         // list of types
00063         //      type            name                    key
00064         { eInput_Button,        "button",               "button" },
00065         { eInput_Joystick,      "joystick",             "joystick" },
00066         { eInput_Pot,           "potentiometer",        "pot" },
00067         { eInput_Dpad,          "directionalPad",       "dpad" },
00068 
00069         // keep this last!
00070         { eInput_Invalid,       NULL,           NULL }
00071 };
00072 
00073 
00074 
00075 ////////////////////////////////////////////////////////////////////////////////
00076 //
00077 //      static helper methods
00078 //
00079 ////////////////////////////////////////////////////////////////////////////////
00080 
00081 ////////////////////////////////////////////////////////////////////////////////
00082 //
00083 //      GType -- class that implements the gamepad::Type interface
00084 //
00085 ////////////////////////////////////////////////////////////////////////////////
00086 
00087 class GType : public Type {
00088 public:
00089         // constructor, destructor ---------------------------------------------
00090         GType(void) throw();
00091         ~GType(void) throw();
00092 
00093         // public class methods ------------------------------------------------
00094         void initialize(IN nstream::Stream * stream);
00095 
00096         // gamepad::Type class interface methods -------------------------------
00097         const char * getId(void) const throw() { return m_id.c_str(); }
00098         const char * getName(void) const throw() { return m_name.c_str(); }
00099         int getInputCount(IN eInputType type) const throw();
00100         const char * getLogicalName(IN eInputType type,
00101                                 IN int idx) const throw();
00102         void getAllInputs(OUT VecString& names) const;
00103         eInputType getInputTypes(IN const char * logicalName) const throw();
00104         int getLogicalIndex(IN const char * logicalName,
00105                                 IN eInputType type) throw();
00106         const char * getMainButton(void) const throw();
00107         smart_ptr<nstream::Stream> getVgfx(void);
00108         void dump(void) const throw();
00109 
00110 private:
00111         // private typedefs ----------------------------------------------------
00112         typedef std::map<eInputType, smart_ptr<VecString> > type_names_t;
00113 
00114         struct type_rec_t {
00115                 type_rec_t(void) throw() { this->clear(); }
00116                 void clear(void) throw() {
00117                                 types = eInput_Invalid;
00118                                 buttonIndex = joyIndex = potIndex = dpadIndex = -1;
00119                         }
00120 
00121                 // data fields
00122                 eInputType      types;
00123                 int             buttonIndex;
00124                 int             joyIndex;
00125                 int             potIndex;
00126                 int             dpadIndex;
00127         };
00128 
00129         typedef std::map<std::string, type_rec_t> type_map_t;
00130 
00131         // private helper methods ----------------------------------------------
00132         void addInputs(IN eInputType type,
00133                                 IN const VecString& names);
00134         int getIndex(IN eInputType type,
00135                                 IN const char * name);
00136 
00137         // private member data -------------------------------------------------
00138         std::string             m_id;
00139         std::string             m_name;
00140         std::string             m_mainButton;   // name of most common used btn
00141         type_names_t            m_names;        // type --> list of names
00142         type_map_t              m_types;        // logical name --> object types
00143         smart_ptr<nstream::File> m_vgfxFile;    // vgfx file (if we have one)
00144 };
00145 
00146 
00147 
00148 GType::GType(void)
00149 throw()
00150 {
00151 }
00152 
00153 
00154 
00155 GType::~GType(void)
00156 throw()
00157 {
00158 }
00159 
00160 
00161 
00162 void
00163 GType::initialize
00164 (
00165 IN nstream::Stream * stream
00166 )
00167 {
00168         ASSERT(stream, "null");
00169         ASSERT_THROW(stream->good(), "bad stream");
00170 
00171         // read the datahash from this stream
00172         std::istream& in = stream->getStream();
00173         ASSERT_THROW(in.good(), "bad stream?");
00174 
00175         smart_ptr<Datahash> logical = readHashFromStream("(root)", in);
00176         ASSERT(logical, "null");
00177 
00178         //DPRINTF("Reading logical gamepad type...");
00179 
00180         m_id = getString(logical, "id");
00181         //DPRINTF("  id: '%s'", m_id.c_str());
00182 
00183         // read the name
00184         m_name = getString(logical, "name");
00185         //DPRINTF("  name: '%s'", m_name.c_str());
00186 
00187         // read the most commonly used (main) button
00188         m_mainButton = getString(logical, "mainButton");
00189         //DPRINTF("  main button: '%s'", m_mainButton.c_str());
00190 
00191         // read the vgfx file (if any)
00192         ASSERT(!m_vgfxFile, "already have vgfx file?");
00193         const char * vgfxFile = getOptionalString(logical, "vgfx", NULL);
00194         if (vgfxFile) {
00195                 smart_ptr<nstream::File> file = stream->getFile();
00196                 ASSERT(file, "null");
00197                 const char * thisFile = file->getName();
00198                 //DPRINTF("Starting with gamepad file: '%s'", thisFile);
00199                 std::string relPath = getPathRelativeTo(thisFile, vgfxFile);
00200                 //DPRINTF("path to vgfx file: '%s'", relPath.c_str());
00201                 smart_ptr<nstream::Manager> mgr = file->getManager();
00202                 ASSERT(mgr, "null");
00203                 m_vgfxFile = mgr->getEntry(relPath.c_str());
00204         }
00205 
00206         // read all input objects
00207         for (const input_entry_t * p = s_inputTypes; p->key; ++p) {
00208                 smart_ptr<VecString> vec = new VecString;
00209                 ASSERT(vec, "out of memory");
00210 
00211                 // any entries with this key at all?
00212                 if (!logical->count(p->key))
00213                         continue;       // skip this one
00214 
00215                 // grab all logical names with this key from input hash
00216                 getStringValues(logical, p->key, *vec);
00217 
00218                 // save in our map
00219                 m_names[p->type] = vec;
00220 
00221                 // update our master map of all names/types
00222                 this->addInputs(p->type, *vec);
00223         }
00224 }
00225 
00226 
00227 
00228 ////////////////////////////////////////////////////////////////////////////////
00229 //
00230 //      GType -- gamepad::Type class interface methods
00231 //
00232 ////////////////////////////////////////////////////////////////////////////////
00233 
00234 int
00235 GType::getInputCount
00236 (
00237 IN eInputType type
00238 )
00239 const
00240 throw()
00241 {
00242         type_names_t::const_iterator i = m_names.find(type);
00243         if (m_names.end() == i) {
00244                 return 0;       // no such type found
00245         }
00246         ASSERT(i->second, "null vector in map");
00247         return i->second->size();
00248 }
00249 
00250 
00251 
00252 const char *
00253 GType::getLogicalName
00254 (
00255 IN eInputType type,
00256 IN int idx
00257 )
00258 const
00259 throw()
00260 {
00261         ASSERT(idx >= 0, "Bad index: %d", idx);
00262 
00263         type_names_t::const_iterator i = m_names.find(type);
00264         if (m_names.end() == i) {
00265                 DPRINTF("cannot get logical name: no such type!");
00266                 return NULL;
00267         }
00268         ASSERT(i->second, "null vector in map");
00269         ASSERT(idx < (int) i->second->size(),
00270             "Input index is too large: %d", idx);
00271 
00272         return i->second->operator[](idx).c_str();
00273 }
00274 
00275 
00276 
00277 void
00278 GType::getAllInputs
00279 (
00280 OUT VecString& logicalNames
00281 )
00282 const
00283 {
00284         logicalNames.clear();
00285 
00286         for (type_map_t::const_iterator i = m_types.begin(); i != m_types.end();
00287              ++i) {
00288                 logicalNames.push_back(i->first);
00289         }
00290 }
00291 
00292 
00293 
00294 eInputType
00295 GType::getInputTypes
00296 (
00297 IN const char * logicalName
00298 )
00299 const
00300 throw()
00301 {
00302         ASSERT(logicalName, "null");
00303 
00304         type_map_t::const_iterator i = m_types.find(logicalName);
00305         if (m_types.end() == i) {
00306                 // logical name not recognized
00307                 return eInput_Invalid;
00308         }
00309         const type_rec_t& tr = i->second;
00310         ASSERT(eInput_Invalid != tr.types, "should be valid type");
00311 
00312         return tr.types;
00313 }
00314 
00315 
00316 
00317 int
00318 GType::getLogicalIndex
00319 (
00320 IN const char * logicalName,
00321 IN eInputType type
00322 )
00323 throw()
00324 {
00325         ASSERT(logicalName, "null");
00326 
00327         type_map_t::iterator i = m_types.find(logicalName);
00328         if (m_types.end() == i) {
00329                 // DPRINTF("Logical name not recognized: '%s'", logicalName);
00330                 return -1;
00331         }
00332 
00333         type_rec_t& tr = i->second;
00334         if (!(type & tr.types)) {
00335                 DPRINTF("Logical name is not of correct type");
00336                 return -1;
00337         }
00338 
00339         // okay this should match!  Look for it...
00340         switch (type) {
00341         case eInput_Button:
00342                 if (-1 == tr.buttonIndex) {
00343                         tr.buttonIndex = getIndex(eInput_Button, logicalName);
00344                 }
00345                 return tr.buttonIndex;
00346 
00347         case eInput_Joystick:
00348                 if (-1 == tr.joyIndex) {
00349                         tr.joyIndex = getIndex(eInput_Joystick, logicalName);
00350                 }
00351                 return tr.joyIndex;
00352 
00353         case eInput_Pot:
00354                 if (-1 == tr.potIndex) {
00355                         tr.potIndex = getIndex(eInput_Pot, logicalName);
00356                 }
00357                 return tr.potIndex;
00358 
00359         case eInput_Dpad:
00360                 if (-1 == tr.dpadIndex) {
00361                         tr.dpadIndex = getIndex(eInput_Dpad, logicalName);
00362                 }
00363                 return tr.dpadIndex;
00364 
00365         default:
00366                 ASSERT(false, "bad type: %d", type);
00367         }
00368 
00369         ASSERT(false, "bad type: %d", type);
00370         return -1;
00371 }
00372 
00373 
00374 
00375 const char *
00376 GType::getMainButton
00377 (
00378 void
00379 )
00380 const
00381 throw()
00382 {
00383         return m_mainButton.c_str();
00384 }
00385 
00386 
00387 
00388 smart_ptr<nstream::Stream>
00389 GType::getVgfx
00390 (
00391 void
00392 )
00393 {
00394         if (!m_vgfxFile) {
00395                 DPRINTF("Gamepad has no vector graphics file");
00396                 return NULL;
00397         }
00398 
00399         // open a stream
00400         smart_ptr<nstream::Stream> stream = m_vgfxFile->openStream();
00401         ASSERT(stream, "null");
00402         ASSERT_THROW(stream->good(), "bad stream?");
00403         return stream;
00404 }
00405 
00406 
00407 
00408 void
00409 GType::dump
00410 (
00411 void
00412 )
00413 const
00414 throw()
00415 {
00416         DPRINTF("Gamepad type: %s", this->getName());
00417         DPRINTF("  Contains %d logical inputs", (int) m_types.size());
00418 
00419         for (type_map_t::const_iterator i = m_types.begin(); i != m_types.end();
00420              ++i) {
00421                 std::string out = i->first;
00422                 out += ":";
00423                 const type_rec_t& tr = i->second;
00424                 for (const input_entry_t * p = s_inputTypes; p->key; ++p) {
00425                         if (tr.types & p->type) {
00426                                 out += " ";
00427                                 out += p->name;
00428                         }
00429                 }
00430                 DPRINTF("    %s", out.c_str());
00431         }
00432 }
00433 
00434 
00435 
00436 ////////////////////////////////////////////////////////////////////////////////
00437 //
00438 //      GType -- private helper methods
00439 //
00440 ////////////////////////////////////////////////////////////////////////////////
00441 
00442 void
00443 GType::addInputs
00444 (
00445 IN eInputType type,
00446 IN const VecString& names
00447 )
00448 {
00449         // update master map of name-->types with given list of named inputs
00450         for (VecString::const_iterator i = names.begin(); i != names.end();
00451              ++i) {
00452                 const char * name = i->c_str();
00453                 type_map_t::iterator k = m_types.find(name);
00454                 if (m_types.end() == k) {
00455                         // this is the first instance of this name we've seen
00456                         type_rec_t tr;
00457                         tr.types = type;
00458                         m_types[name] = tr;
00459                 } else {
00460                         // name is already in map, update type
00461                         type_rec_t& tr = k->second;
00462                         tr.types = (eInputType) (tr.types | type);
00463                 }
00464         }
00465 }
00466 
00467 
00468 
00469 int
00470 GType::getIndex
00471 (
00472 IN eInputType type,
00473 IN const char * logicalName
00474 )
00475 {
00476         ASSERT(eInput_Invalid != type, "invalid type");
00477         ASSERT(logicalName, "null");
00478 
00479         type_names_t::iterator i = m_names.find(type);
00480         ASSERT_THROW(m_names.end() != i, "bad type: " << type);
00481 
00482         const VecString * names = i->second;
00483         ASSERT(names, "null");
00484         int nNames = names->size();
00485         for (int j = 0; j < nNames; ++j) {
00486                 const char * name = names->operator[](j).c_str();
00487                 if (!strcmp(name, logicalName)) {
00488                         return j;
00489                 }
00490         }
00491 
00492         // not found in list!
00493         ASSERT(false, "logical name not in type list");
00494         return -1;
00495 }
00496 
00497 
00498 
00499 ////////////////////////////////////////////////////////////////////////////////
00500 //
00501 //      public API
00502 //
00503 ////////////////////////////////////////////////////////////////////////////////
00504 
00505 smart_ptr<Type>
00506 readType
00507 (
00508 IN nstream::Stream * stream
00509 )
00510 {
00511         ASSERT(stream, "null");
00512 
00513         // create a gamepad object based on the logical configuration
00514         smart_ptr<GType> local = new GType;
00515         ASSERT(local, "out of memory");
00516         local->initialize(stream);
00517 
00518         // all done!
00519         return local;
00520 }
00521 
00522 
00523 
00524 void
00525 addGamepadTypesInFolderToMap
00526 (
00527 IO nstream::Folder * folder,
00528 IO gamepad_type_map_t& map
00529 )
00530 {
00531         ASSERT(folder, "null");
00532 
00533         // loop through all entries in the folder
00534         folder->resetIteration();
00535         for (;;) {
00536                 smart_ptr<nstream::Entry> entry = folder->getNextChild();
00537                 if (!entry)
00538                         break;          // end of iteration
00539 
00540                 // upcast to file object
00541                 smart_ptr<nstream::File> file = entry;
00542                 if (!file)
00543                         continue;       // skip any non-file objects
00544 
00545                 // is this a gamepad file?
00546                 if (!hasExtension(file->getName(), s_extension))
00547                         continue;       // not a gamepad file
00548 
00549                 // get stream
00550                 smart_ptr<nstream::Stream> stream = file->openStream();
00551 
00552                 try {
00553                         THROW(stream, "failed to open stream");
00554                         THROW(stream->good(), "bad stream");
00555 
00556                         smart_ptr<Type> gt = readType(stream);
00557                         THROW(gt, "null gamepad type object returned");
00558                         map[gt->getName()] = gt;
00559 
00560                 } catch (std::exception& e) {
00561                         WAVE_EX(wex);
00562                         wex << "Failed to read gamepad stream: '";
00563                         wex << file->getName() << "' in folder '";
00564                         wex << folder->getName() << "'\n";
00565                         wex << e.what();
00566                 }
00567         }
00568 }
00569 
00570 
00571 
00572 };      // gamepad namespace
00573