Back to index

supertuxkart  0.5+dfsg1
user_config.cpp
Go to the documentation of this file.
00001 // $Id: user_config.cpp 2111 2008-05-31 07:04:30Z cosmosninja $
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  Copyright (C) 2006 SuperTuxKart-Team
00005 //  Modelled after Supertux's configfile.cpp
00006 //
00007 //  This program is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU General Public License
00009 //  as published by the Free Software Foundation; either version 2
00010 //  of the License, or (at your option) any later version.
00011 //
00012 //  This program is distributed in the hope that it will be useful,
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //  GNU General Public License for more details.
00016 //
00017 //  You should have received a copy of the GNU General Public License
00018 //  along with this program; if not, write to the Free Software
00019 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020 #include <stdio.h>
00021 
00022 #include <stdexcept>
00023 #include <sstream>
00024 #include <string>
00025 
00026 // for mkdir:
00027 #if !defined(WIN32) || defined(__CYGWIN__)
00028 #  include <sys/stat.h>
00029 #  include <sys/types.h>
00030 #else
00031 #  include <direct.h>
00032 #endif
00033 
00034 #include <SDL/SDL.h>
00035 #include <plib/ul.h>
00036 
00037 #include "actionmap.hpp"
00038 #include "user_config.hpp"
00039 #include "lisp/lisp.hpp"
00040 #include "lisp/parser.hpp"
00041 #include "lisp/writer.hpp"
00042 #include "translation.hpp"
00043 #include "unlock_manager.hpp"
00044 #include "race_manager.hpp"
00045 #include "file_manager.hpp"
00046 #if defined(WIN32) && !defined(__CYGWIN__)
00047 #  define snprintf _snprintf
00048 #endif
00049 
00050 using namespace std;
00051 using namespace lisp;
00052 
00053 UserConfig *user_config;
00054 
00055 UserConfig::UserConfig()
00056 {
00057     setDefaults();
00058     loadConfig();
00059 }   // UserConfig
00060 
00061 // -----------------------------------------------------------------------------
00062 UserConfig::UserConfig(const std::string& filename)
00063 {
00064     setDefaults();
00065     loadConfig(filename);
00066 }   // UserConfig
00067 
00068 
00069 // -----------------------------------------------------------------------------
00070 UserConfig::~UserConfig()
00071 {
00072 }
00073 
00074 // -----------------------------------------------------------------------------
00078 void UserConfig::setFilename()
00079 {
00080 #ifdef WIN32
00081     m_filename = file_manager->getLogFile("supertuxkart.cfg");
00082 #else
00083     m_filename = file_manager->getLogFile("config");
00084 #endif
00085 }   // setFilename
00086 
00087 // -----------------------------------------------------------------------------
00091 void UserConfig::setDefaults()
00092 {
00093     setFilename();
00094     m_warning          = "";
00095     m_keyboard_debug   = false;
00096     m_track_debug      = 0;
00097     m_bullet_debug     = false;
00098     m_fullscreen       = false;
00099     m_no_start_screen  = false;
00100     m_sfx              = UC_ENABLE;
00101     m_music            = UC_ENABLE;
00102     m_smoke            = false;
00103     m_display_fps      = false;
00104     m_herring_style    = "new";
00105     m_background_music = "";
00106     m_profile          = 0;
00107     m_skidding         = false;
00108     m_max_fps          = 120;
00109     m_sfx_volume       = 1.0f;
00110     m_use_kph          = false;
00111     m_replay_history   = false;
00112     m_width            = 800;
00113     m_height           = 600;
00114     m_prev_width        = m_width;
00115     m_prev_height       = m_height;
00116     m_prev_windowed     = false;
00117     m_crashed           = false;
00118     m_blacklist_res.clear();
00119     m_karts            = 4;
00120     m_log_errors       = false;
00121 
00122     if(getenv("USERNAME")!=NULL)        // for windows
00123         m_username=getenv("USERNAME");
00124     else if(getenv("USER")!=NULL)       // Linux, Macs
00125         m_username=getenv("USER");
00126     else if(getenv("LOGNAME")!=NULL)    // Linux, Macs
00127         m_username=getenv("LOGNAME");
00128     else m_username="nouser";
00129 
00130     // Set the name as the default name for all players.
00131     for(int i=0; i<4; i++) 
00132     {
00133         m_player[i].setName(m_username);
00134     }
00135        
00136        // Clear every entry.
00137        memset(inputMap, 0, sizeof(inputMap));
00138 
00139        /* general game input settings */
00140        set(GA_ENTER,
00141               Input(IT_KEYBOARD, SDLK_RETURN),
00142               Input(IT_KEYBOARD, SDLK_SPACE),
00143               Input(IT_STICKBUTTON, 0, 0),
00144               Input(IT_MOUSEBUTTON, 1));
00145        set(GA_LEAVE,
00146               Input(IT_KEYBOARD, SDLK_ESCAPE),
00147               Input(IT_STICKBUTTON, 0, 1),
00148               Input(IT_MOUSEBUTTON, 2),
00149               Input(IT_MOUSEBUTTON, 3));
00150        set(GA_CURSOR_UP,
00151               Input(IT_KEYBOARD, SDLK_UP),
00152               Input(IT_STICKMOTION, 0, 1, AD_NEGATIVE));
00153        
00154        set(GA_CURSOR_DOWN,
00155               Input(IT_KEYBOARD, SDLK_DOWN),
00156               Input(IT_STICKMOTION, 0, 1, AD_POSITIVE));
00157        
00158        set(GA_CURSOR_LEFT,
00159               Input(IT_KEYBOARD, SDLK_LEFT),
00160               Input(IT_STICKMOTION, 0, 0, AD_NEGATIVE));
00161 
00162        set(GA_CURSOR_RIGHT,
00163               Input(IT_KEYBOARD, SDLK_RIGHT),
00164               Input(IT_STICKMOTION, 0, 0, AD_POSITIVE));
00165 
00166        set(GA_CLEAR_MAPPING,
00167               Input(IT_KEYBOARD, SDLK_BACKSPACE),
00168               Input(IT_STICKBUTTON, 0, 2));
00169 
00170        set(GA_INC_SCROLL_SPEED,
00171               Input(IT_KEYBOARD, SDLK_PLUS));
00172        set(GA_INC_SCROLL_SPEED_FAST,
00173               Input(IT_KEYBOARD, SDLK_PAGEDOWN));
00174 
00175        set(GA_DEC_SCROLL_SPEED,
00176               Input(IT_KEYBOARD, SDLK_MINUS));
00177        set(GA_DEC_SCROLL_SPEED_FAST,
00178               Input(IT_KEYBOARD, SDLK_PAGEUP));
00179 
00180        set(GA_TOGGLE_FULLSCREEN,
00181               Input(IT_KEYBOARD, SDLK_F9));
00182        set(GA_LEAVE_RACE,
00183               Input(IT_KEYBOARD, SDLK_ESCAPE));
00184 #ifdef DEBUG
00185        set(GA_DEBUG_ADD_SPARK,
00186               Input(IT_KEYBOARD, SDLK_F1));
00187        set(GA_DEBUG_ADD_MISSILE,
00188               Input(IT_KEYBOARD, SDLK_F2));
00189        set(GA_DEBUG_ADD_HOMING,
00190               Input(IT_KEYBOARD, SDLK_F3));
00191 #endif
00192        set(GA_DEBUG_TOGGLE_FPS,
00193               Input(IT_KEYBOARD, SDLK_F12));
00194        set(GA_DEBUG_TOGGLE_WIREFRAME,
00195               Input(IT_KEYBOARD, SDLK_F11));
00196        set(GA_DEBUG_HISTORY,
00197               Input(IT_KEYBOARD, SDLK_F10));
00198               
00199        // TODO: The following should become a static
00200        // array. This allows:
00201        // a) resetting to default values
00202        // b) prevent loading those defaults if config file contains any bindings
00203 
00204     /* Player 1 default input settings */
00205     set(GA_P1_LEFT,
00206               Input(IT_KEYBOARD, SDLK_LEFT));
00207     set(GA_P1_RIGHT,
00208               Input(IT_KEYBOARD, SDLK_RIGHT));
00209     set(GA_P1_ACCEL,
00210               Input(IT_KEYBOARD, SDLK_UP));
00211     set(GA_P1_BRAKE,
00212               Input(IT_KEYBOARD, SDLK_DOWN));
00213     set(GA_P1_WHEELIE,
00214               Input(IT_KEYBOARD, SDLK_RSHIFT));
00215     set(GA_P1_JUMP,
00216               Input(IT_KEYBOARD, SDLK_MINUS));
00217     set(GA_P1_RESCUE,
00218               Input(IT_KEYBOARD, SDLK_BACKSPACE));
00219     set(GA_P1_FIRE,
00220               Input(IT_KEYBOARD, SDLK_RCTRL));
00221     set(GA_P1_LOOK_BACK,
00222               Input(IT_KEYBOARD, SDLK_RALT));
00223 
00224     /* Player 2 default input settings */
00225     set(GA_P2_LEFT,
00226               Input(IT_KEYBOARD, SDLK_a));
00227     set(GA_P2_RIGHT,
00228               Input(IT_KEYBOARD, SDLK_d));
00229     set(GA_P2_ACCEL,
00230               Input(IT_KEYBOARD, SDLK_w));
00231     set(GA_P2_BRAKE,
00232               Input(IT_KEYBOARD, SDLK_s));
00233     set(GA_P2_WHEELIE,
00234               Input(IT_KEYBOARD, SDLK_LSHIFT));
00235     set(GA_P2_JUMP,
00236               Input(IT_KEYBOARD, SDLK_CAPSLOCK));
00237     set(GA_P2_RESCUE,
00238               Input(IT_KEYBOARD, SDLK_q));
00239     set(GA_P2_FIRE,
00240               Input(IT_KEYBOARD, SDLK_LCTRL));
00241     set(GA_P2_LOOK_BACK,
00242               Input(IT_KEYBOARD, SDLK_LALT));
00243 
00244     /* Player 3 default input settings */
00245     set(GA_P3_LEFT,
00246               Input(IT_KEYBOARD, SDLK_f));
00247     set(GA_P3_RIGHT,
00248               Input(IT_KEYBOARD, SDLK_h));
00249     set(GA_P3_ACCEL,
00250               Input(IT_KEYBOARD, SDLK_t));
00251     set(GA_P3_BRAKE,
00252               Input(IT_KEYBOARD, SDLK_g));
00253     set(GA_P3_WHEELIE,
00254               Input(IT_KEYBOARD, SDLK_c));
00255     set(GA_P3_JUMP,
00256               Input(IT_KEYBOARD, SDLK_v));
00257     set(GA_P3_RESCUE,
00258               Input(IT_KEYBOARD, SDLK_r));
00259     set(GA_P3_FIRE,
00260               Input(IT_KEYBOARD, SDLK_b));
00261     set(GA_P3_LOOK_BACK,
00262               Input(IT_KEYBOARD, SDLK_n));
00263 
00264     /* Player 4 default input settings  */
00265     set(GA_P4_LEFT,
00266               Input(IT_KEYBOARD, SDLK_j));
00267     set(GA_P4_RIGHT,
00268               Input(IT_KEYBOARD, SDLK_l));
00269     set(GA_P4_ACCEL,
00270               Input(IT_KEYBOARD, SDLK_i));
00271     set(GA_P4_BRAKE,
00272               Input(IT_KEYBOARD, SDLK_k));
00273     set(GA_P4_WHEELIE,
00274               Input(IT_KEYBOARD, SDLK_m));
00275     set(GA_P4_JUMP,
00276               Input(IT_KEYBOARD, SDLK_COMMA));
00277     set(GA_P4_RESCUE,
00278               Input(IT_KEYBOARD, SDLK_u));
00279     set(GA_P4_FIRE,
00280               Input(IT_KEYBOARD, SDLK_PERIOD));
00281     set(GA_P4_LOOK_BACK,
00282               Input(IT_KEYBOARD, SDLK_SLASH));
00283 
00284 }   // setDefaults
00285 
00286 
00287 // -----------------------------------------------------------------------------
00291 void UserConfig::loadConfig()
00292 {
00293     loadConfig(m_filename);
00294 }   // loadConfig
00295 
00296 // -----------------------------------------------------------------------------
00304 int UserConfig::CheckAndCreateDir()
00305 {
00306     const std::string DIRNAME = file_manager->getHomeDir();
00307     ulDir*      u       = ulOpenDir(DIRNAME.c_str());
00308     if(u)
00309     {  // OK, directory exists
00310         ulCloseDir(u);
00311         return 1;
00312     }
00313     // The directory does not exist, try to create it
00314     int bError;
00315 #if defined(WIN32) && !defined(__CYGWIN__)
00316     bError = _mkdir(DIRNAME.c_str()      ) != 0;
00317 #else
00318     bError = mkdir(DIRNAME.c_str(), 0755) != 0;
00319 #endif
00320     if(bError)
00321     {
00322         fprintf(stderr, "Couldn't create '%s', config files will not be saved.\n",
00323                 DIRNAME.c_str());
00324         return 0;
00325     }
00326     else
00327     {
00328         printf("Config directory '%s' successfully created.\n",DIRNAME.c_str());
00329         return 2;
00330     }
00331 
00332 }   // CheckAndCreateDir
00333 
00334 // -----------------------------------------------------------------------------
00336 void UserConfig::loadConfig(const std::string& filename)
00337 {
00338     std::string temp;
00339     const lisp::Lisp* root = 0;
00340     int i = 0;
00341     int dirExist = CheckAndCreateDir();
00342     // Check if the config directory exists. If not, exit without an error
00343     // message, an appropriate message was printed by CheckAndCreateDir
00344     if (dirExist != 1) return;
00345 
00346     try
00347     {
00348         lisp::Parser parser;
00349         root = parser.parse(filename);
00350     }
00351     catch(std::exception& e)
00352     {
00353         (void)e;  // avoid warning about unreferenced local variable
00354         printf("Config file '%s' does not exist, it will be created.\n", 
00355                filename.c_str());
00356         delete root;
00357         return;
00358     }
00359 
00360     try
00361     {
00362         const lisp::Lisp* lisp = root->getLisp("tuxkart-config");
00363         if(!lisp) 
00364         {
00365             char msg[MAX_ERROR_MESSAGE_LENGTH];
00366             snprintf(msg, sizeof(msg), "No tuxkart-config node");
00367             throw std::runtime_error(msg);
00368         }
00369         int configFileVersion = 0;
00370         lisp->get("configFileVersion", configFileVersion);
00371         if (configFileVersion < CURRENT_CONFIG_VERSION)
00372         {
00373             // Give some feedback to the user about what was changed.
00374             // Do NOT add a break after the case, so that all changes will be printed
00375             printf("\nConfig file version '%d' is too old.\n"
00376                      "The following changes have been applied in the current SuperTuxKart version:\n",
00377                    configFileVersion);
00378             int needToAbort=0;
00379             switch(configFileVersion)
00380             {
00381             case 0:  printf("- Single window menu, old status display,new keyboard style settings were removed\n");
00382                      needToAbort=std::max(needToAbort,0);
00383             case 1:  printf("- Key bindings were changed, please check the settings. All existing values were discarded.\n");
00384                      needToAbort=std::max(needToAbort,1);// old keybinds wouldn't make any sense
00385             case 2:  printf("Added username, using: '%s'.\n", m_username.c_str());
00386                      needToAbort=std::max(needToAbort,0);
00387             case 3:  printf("Added username for all players.\n");
00388                      needToAbort=std::max(needToAbort,0);
00389             case 4:  printf("Added jumping, which invalidates all key bindings.\n");
00390                      needToAbort=std::max(needToAbort,0);
00391             case 99: break;
00392             default: printf("Config file version '%d' is too old. Discarding your configuration. Sorry. :(\n", configFileVersion);
00393                      needToAbort=1;
00394                      break;
00395             }
00396             if(needToAbort)
00397             {
00398                 printf("The old config file is deleted, a new one will be created.\n");
00399                 delete root;
00400                 return;
00401             }
00402             printf("This warning can be ignored, the config file will be automatically updated.\n");
00403             // Keep on reading the config files as far as possible
00404         }   // if configFileVersion<SUPPORTED_CONFIG_VERSION
00405 
00406         /*get toggles*/
00407         lisp->get("fullscreen",       m_fullscreen);
00408         bool doSFX=false;                                // avoid warning
00409         lisp->get("sfx" ,             doSFX);
00410         m_sfx = doSFX ? UC_ENABLE : UC_DISABLE;
00411         lisp->get("nostartscreen",    m_no_start_screen);
00412         bool doMusic=false;                              // avoid warning
00413         lisp->get("music",            doMusic);
00414         m_music = doMusic ? UC_ENABLE : UC_DISABLE;
00415         lisp->get("smoke",            m_smoke);
00416         lisp->get("displayFPS",       m_display_fps);
00417         lisp->get("herringStyle",     m_herring_style);
00418         lisp->get("background-music", m_background_music);
00419         lisp->get("max-fps",          m_max_fps);
00420         lisp->get("sfx-volume",       m_sfx_volume);
00421         lisp->get("useKPH",           m_use_kph);
00422 
00423         /*get resolution width/height*/
00424         lisp->get("width",            m_width);
00425         lisp->get("height",           m_height);
00426         lisp->get("prev_width",             m_prev_width);
00427         lisp->get("prev_height",     m_prev_height);
00428         lisp->get("prev_windowed",   m_prev_windowed);
00429         //detect if resolution change previously crashed STK
00430         lisp->get("crash_detected",         m_crashed);
00431         // blacklisted resolutions
00432         lisp->getVector("blacklisted_resolutions", 
00433                                       m_blacklist_res);
00434         /*get number of karts*/
00435         lisp->get("karts",            m_karts);
00436 
00437         //get whether to log errors to file
00438         lisp->get("log-errors",       m_log_errors);
00439               
00440               // Handle loading the stick config in it own method.
00441               readStickConfigs(lisp);
00442 
00443         // Unlock information:
00444         const lisp::Lisp* unlock_info = lisp->getLisp("unlock-info");
00445         if(unlock_info) unlock_manager->load(unlock_info);
00446 
00447         /*get player configurations*/
00448         for(i=0; i<PLAYERS; ++i)
00449         {
00450             temp = "player-";
00451             temp += i+'1';
00452 
00453             const lisp::Lisp* reader = lisp->getLisp(temp);
00454             if(!reader)
00455             {
00456                 char msg[MAX_ERROR_MESSAGE_LENGTH];
00457                 snprintf(msg, sizeof(msg), "No '%s' node", temp.c_str());
00458                 throw std::runtime_error(msg);
00459             }
00460             std::string name;
00461             reader->get("name", name);
00462             if (configFileVersion <= 3) 
00463             {
00464                 // For older config files, replace the default player 
00465                 // names "Player %d" with the user name
00466                 char sDefaultName[10];
00467                 snprintf(sDefaultName, sizeof(sDefaultName),
00468                          "Player %d",i+1);
00469                 // If the config file does not contain a name or the old
00470                 // default name, set the default username as player name.
00471                 if(name.size()==0 || name==sDefaultName) name=m_username;
00472             }
00473             m_player[i].setName(name);
00474 
00475             int lastKartId = 0;
00476             reader->get("lastKartId", lastKartId);
00477             m_player[i].setLastKartId(lastKartId);
00478 
00479             // Don't read the key bindings from a config file earlier than 
00480             // version 5. These config files contain (unused) keybindings for 
00481             // jumping, so it is possible that the same key is used for 
00482             // jumping for player 1 and something else for another player.
00483             // In this case jumping for player one would be disabled (see
00484             // unsetDuplicates). To be on the safe side, old key bindings 
00485             // are just discarded.
00486             if (configFileVersion <= 4) 
00487             {
00488                 m_warning=_("Old config file found, check your key bindings!");
00489             }  // configFileVersion <= 4
00490 
00491             // Retrieves a player's INPUT configuration
00492             readPlayerInput(reader, "left",     KA_LEFT,      i);
00493             readPlayerInput(reader, "right",    KA_RIGHT,     i);
00494             readPlayerInput(reader, "accel",    KA_ACCEL,     i);
00495             readPlayerInput(reader, "brake",    KA_BRAKE,     i);
00496             readPlayerInput(reader, "wheelie",  KA_WHEELIE,   i);
00497             readPlayerInput(reader, "jump",     KA_JUMP,      i);
00498             readPlayerInput(reader, "rescue",   KA_RESCUE,    i);
00499             readPlayerInput(reader, "fire",     KA_FIRE,      i);
00500             readPlayerInput(reader, "lookBack", KA_LOOK_BACK, i);
00501         }   // for i < PLAYERS
00502     }
00503     catch(std::exception& e)
00504     {
00505         fprintf(stderr, "Error while parsing config '%s':\n", filename.c_str());
00506         fprintf(stderr,  e.what());
00507         fprintf(stderr, "\n");
00508     }
00509 
00510 
00511     delete root;
00512 }   // loadConfig
00513 
00514 // -----------------------------------------------------------------------------
00515 
00516 void
00517 UserConfig::readStickConfigs(const lisp::Lisp *r)
00518 {
00519        string temp;
00520        int count = 0;
00521        
00522        const lisp::Lisp *scsreader = r->getLisp("stick-configs");
00523        if (scsreader)
00524        {
00525               scsreader->get("count", count);
00526                      
00527               for (int i = 0; i < count; i++)
00528               {
00529                      temp = "stick-";
00530                      temp += (i + '1');
00531                      const lisp::Lisp *screader = scsreader->getLisp(temp);
00532                      if (screader)
00533                      {
00534                             string *id = new string();
00535                             screader->get("id", *id);
00536                             
00537                             StickConfig *sc = new StickConfig(*id);
00538                             
00539                             screader->get("preferredIndex", sc->preferredIndex);
00540                             screader->get("deadzone", sc->deadzone);
00541                             
00542                             m_stickconfigs.push_back(sc);
00543                      }
00544               }
00545               
00546        }
00547 
00548 }
00549 
00550 // -----------------------------------------------------------------------------
00551 void
00552 UserConfig::readPlayerInput(const lisp::Lisp *r, const char *node,
00553                                                  KartAction ka, int playerIndex)
00554 {
00555        readInput(r, node, (GameAction) (playerIndex * KC_COUNT + ka + GA_P1_LEFT));
00556 }
00557 // -----------------------------------------------------------------------------
00558 void
00559 UserConfig::readInput(const lisp::Lisp* r,
00560                                      const char *node,
00561                       GameAction action)
00562 {
00563     string inputTypeName;
00564 
00565     const Lisp* nodeReader = r->getLisp(node);
00566     if (!nodeReader)
00567       return;
00568 
00569     // Every unused id variable *must* be set to
00570     // something different than -1. Otherwise
00571     // the restored mapping will not be applied.
00572     Input input = Input(IT_NONE, -1, -1, -1);
00573 
00574     nodeReader->get("type", inputTypeName);
00575     if (inputTypeName == "keyboard")
00576     {
00577         input.type = IT_KEYBOARD;
00578         nodeReader->get("key", input.id0);
00579         input.id1 = input.id2 = 0;
00580     }
00581     else if (inputTypeName == "stickaxis")
00582     {
00583         input.type = IT_STICKMOTION;
00584         nodeReader->get("stick", input.id0);
00585         nodeReader->get("axis", input.id1);
00586         nodeReader->get("direction", input.id2);
00587     }
00588     else if (inputTypeName == "stickbutton")
00589     {
00590         input.type = IT_STICKBUTTON;
00591         nodeReader->get("stick", input.id0);
00592         nodeReader->get("button", input.id1);
00593         input.id2 = 0;
00594     }
00595     else if (inputTypeName == "stickhat")
00596     {
00597         input.type = IT_STICKHAT;
00598         // TODO: Implement me
00599     }
00600     else if (inputTypeName == "mouseaxis")
00601     {
00602         input.type = IT_MOUSEMOTION;
00603         nodeReader->get("axis", input.id0);
00604         nodeReader->get("direction", input.id1);
00605         input.id2 = 0;
00606     }
00607     else if (inputTypeName == "mousebutton")
00608     {
00609         input.type = IT_MOUSEBUTTON;
00610         nodeReader->get("button", input.id0);
00611         input.id1 = input.id2 = 0;
00612     }
00613 
00614     if (input.id0 != -1 && input.id1 != -1 && input.id2 != -1)
00615        {
00616         setInput(action, input);
00617        }
00618 
00619 }
00620 
00621 // -----------------------------------------------------------------------------
00623 void UserConfig::saveConfig()
00624 {
00625     saveConfig(m_filename);
00626 }   // saveConfig
00627 
00628 // -----------------------------------------------------------------------------
00630 void UserConfig::saveConfig(const std::string& filename)
00631 {
00632     std::string temp;
00633     int i;
00634 
00635     const int DIR_EXIST = CheckAndCreateDir();
00636     // Check if the config directory exists (again, since it was already checked
00637     // when reading the config file - this is done in case that the problem was
00638     // fixed while tuxkart is running). If the directory does not exist and
00639     // can not be created, an error message was already printed to stderr,
00640     // and we can exit here without any further messages.
00641     if (DIR_EXIST == 0) return;
00642 
00643     Writer *writer = new Writer(filename);
00644     try
00645     {
00646         writer->beginList("tuxkart-config");
00647         writer->writeComment("If the game's supported config file version is higher than this number the configuration is discarded.");
00648         writer->write("configFileVersion\t",   CURRENT_CONFIG_VERSION);
00649 
00650         writer->writeComment("the following options can be set to #t or #f:");
00651         writer->write("sfx\t",   !(m_sfx==UC_DISABLE));
00652         writer->write("music\t", !(m_music==UC_DISABLE));
00653         writer->write("smoke\t", m_smoke);
00654         writer->writeComment("Display frame per seconds");
00655         writer->write("displayFPS\t", m_display_fps);
00656         writer->writeComment("Name of the .herring file to use.");
00657         writer->write("herringStyle\t", m_herring_style);
00658         writer->writeComment("Background music file to use,");
00659         writer->write("background-music\t", m_background_music);
00660         writer->writeComment("Use of kilometers per hours (km/h) instead of mph");
00661         writer->write("useKPH\t", m_use_kph);
00662         writer->writeComment("maximum fps, should be at least 60");
00663         writer->write("max-fps\t", m_max_fps);
00664         writer->writeComment("Volume for sound effects, see openal AL_GAIN for interpretation");
00665         writer->write("sfx-volume", m_sfx_volume);
00666 
00667         writer->writeComment("screen resolution and windowing mode");
00668         writer->write("width\t", m_width);
00669         writer->write("height\t", m_height);
00670         writer->write("prev_width\t", m_prev_width);
00671         writer->write("prev_height\t", m_prev_height);
00672         writer->write("prev_windowed\t", m_prev_windowed);
00673         writer->write("crash_detected\t", m_crashed);
00674         writer->write("blacklisted_resolutions\t", m_blacklist_res);
00675         writer->write("fullscreen\t", m_fullscreen);
00676 
00677         writer->writeComment("number of karts. -1 means use all");
00678         writer->write("karts\t", m_karts);
00679 
00680         writer->writeComment("number of karts. -1 means use all");
00681         writer->write("karts\t", m_karts);
00682         
00683         writer->writeComment("error logging to log (true) or stderr (false)");
00684         writer->write("log-errors\t", m_log_errors);
00685               
00686               writeStickConfigs(writer);
00687 
00688         // Write unlock information back
00689         writer->beginList("unlock-info");
00690         unlock_manager->save(writer);
00691         writer->endList("unlock-info");
00692 
00693         /* write player configurations */
00694         for(i=0; i<PLAYERS; ++i)
00695         {
00696             temp = "player ";
00697             temp += i+'1';
00698             temp += " settings";
00699             writer->writeComment(temp);
00700             temp = "player-";
00701             temp += i+'1';
00702             writer->beginList(temp);
00703 
00704             writer->write("name\t", m_player[i].getName());
00705 
00706             writer->writeComment("optional");
00707             writer->write("lastKartId", m_player[i].getLastKartId());
00708 
00709             writePlayerInput(writer, "left\t", KA_LEFT, i);
00710             writePlayerInput(writer, "right\t", KA_RIGHT, i);
00711             writePlayerInput(writer, "accel\t", KA_ACCEL, i);
00712             writePlayerInput(writer, "brake\t", KA_BRAKE, i);
00713             writePlayerInput(writer, "wheelie\t", KA_WHEELIE, i);
00714             writePlayerInput(writer, "jump\t", KA_JUMP, i);
00715             writePlayerInput(writer, "rescue\t", KA_RESCUE, i);
00716             writePlayerInput(writer, "fire\t", KA_FIRE, i);
00717             writePlayerInput(writer, "lookBack\t", KA_LOOK_BACK, i);
00718 
00719             writer->endList(temp);
00720         }   // for i
00721 
00722         writer->endList("tuxkart-config");
00723     }
00724     catch(std::exception& e)
00725     {
00726         fprintf(stderr, "Couldn't write config: ");
00727         fprintf(stderr, e.what());
00728         fprintf(stderr, "\n");
00729     }
00730        
00731        delete writer;
00732 }   // saveConfig
00733 
00734 // -----------------------------------------------------------------------------
00735 
00736 void
00737 UserConfig::writeStickConfigs(lisp::Writer *writer)
00738 {
00739        int count = 0;
00740        string temp;
00741        
00742        writer->beginList("stick-configs");
00743        
00744        count = (int)m_stickconfigs.size();
00745        writer->write("count", count);
00746        
00747        for (int i = 0; i < count; i++)
00748        {
00749               StickConfig *sc = m_stickconfigs[i];
00750               temp = "stick-";
00751               temp += i + '1';
00752               
00753               writer->beginList(temp);
00754                             
00755               writer->write("id", sc->id);
00756               writer->write("preferredIndex", sc->preferredIndex);
00757               writer->writeComment("0 means that the default deadzone value is used.");
00758               writer->write("deadzone", sc->deadzone);
00759               
00760               writer->endList(temp);
00761        }
00762        
00763        writer->endList("stick-configs");
00764 }
00765 
00766 // -----------------------------------------------------------------------------
00767 void
00768 UserConfig::writePlayerInput(lisp::Writer *writer, const char *node,
00769                                                   KartAction ka, int playerIndex)
00770 {
00771        writeInput(writer, node, (GameAction) (playerIndex * KC_COUNT + ka + GA_P1_LEFT));
00772 }
00773 // -----------------------------------------------------------------------------
00774 void
00775 UserConfig::writeInput(lisp::Writer *writer,
00776                                       const char *node,
00777                                       GameAction action)
00778 {
00779     writer->beginList(node);
00780        
00781        if (inputMap[action].count)
00782        {
00783 
00784     const Input input = inputMap[action].inputs[0];
00785 
00786     if (input.type != IT_NONE)
00787     {
00788               switch (input.type)
00789               {
00790               case IT_NONE:
00791                      break;
00792               case IT_KEYBOARD:
00793                      writer->write("type", "keyboard");
00794                      writer->write("key", input.id0);
00795                      break;
00796               case IT_STICKMOTION:
00797                      writer->write("type", "stickaxis");
00798                      writer->write("stick", input.id0);
00799                      writer->write("axis", input.id1);
00800                      writer->writeComment("0 is negative/left/up, 1 is positive/right/down");
00801                      writer->write("direction", input.id2);
00802                      break;
00803               case IT_STICKBUTTON:
00804                      writer->write("type", "stickbutton");
00805                      writer->write("stick", input.id0);
00806                      writer->write("button", input.id1);
00807                      break;
00808               case IT_STICKHAT:
00809                      // TODO: Implement me
00810                      break;
00811               case IT_MOUSEMOTION:
00812                      writer->write("type", "mouseaxis");
00813                      writer->write("axis", input.id0);
00814                      writer->writeComment("0 is negative/left/up, 1 is positive/right/down");
00815                      writer->write("direction", input.id1);
00816                      break;
00817               case IT_MOUSEBUTTON:
00818                      writer->write("type", "mousebutton");
00819                      writer->writeComment("0 is left, 1 is middle, 2 is right, 3 is wheel up, 4 is wheel down");
00820                      writer->writeComment("other values denote auxillary buttons");
00821                      writer->write("button", input.id0);
00822                      break;
00823               }
00824        }
00825        }
00826 
00827     writer->endList(node);
00828 }
00829 // -----------------------------------------------------------------------------
00830 std::string UserConfig::getInputAsString(Input &input)
00831 {
00832     char msg[MAX_MESSAGE_LENGTH];
00833     std::ostringstream stm;
00834     
00835     switch (input.type)
00836     {
00837     case IT_NONE:
00838         snprintf(msg, sizeof(msg), _("not set"));
00839         break;
00840     case IT_KEYBOARD:
00841         snprintf(msg, sizeof(msg), "%s", SDL_GetKeyName((SDLKey) input.id0));
00842         break;
00843     case IT_STICKMOTION:
00844         snprintf(msg, sizeof(msg), _("joy %d axis %d  %c"),
00845                  input.id0, input.id1, (input.id2 == AD_NEGATIVE) ? '-' : '+');
00846         break;
00847     case IT_STICKBUTTON:
00848         snprintf(msg, sizeof(msg), _("joy %d btn %d"), input.id0, input.id1);
00849         break;
00850     case IT_STICKHAT:
00851         snprintf(msg, sizeof(msg), _("joy %d hat %d"), input.id0, input.id1);
00852         break;
00853     case IT_MOUSEBUTTON:
00854         snprintf(msg, sizeof(msg), _("mouse btn %d"), input.id0);
00855         break;
00856     case IT_MOUSEMOTION:
00857         snprintf(msg, sizeof(msg), _("mouse axis %d %c"),
00858                  input.id0, ((input.id1 == AD_NEGATIVE) ? '-' : '+'));
00859         break;
00860     default:
00861         snprintf(msg, sizeof(msg), _("Invalid"));
00862     }
00863     
00864     stm << msg;
00865     
00866     return stm.str();
00867 }   // GetKeyAsString
00868 
00869 // -----------------------------------------------------------------------------
00870 string
00871 UserConfig::getMappingAsString(GameAction ga)
00872 {
00873   if (inputMap[ga].count
00874          && inputMap[ga].inputs[0].type)
00875   {
00876     stringstream s;
00877     s << getInputAsString(inputMap[ga].inputs[0]);
00878 
00879        return s.str();
00880   }
00881   else
00882   {
00883     return string(_("not set"));
00884   }
00885 }
00886 // -----------------------------------------------------------------------------
00887 string
00888 UserConfig::getMappingAsString(int playerIndex, KartAction ka)
00889 {
00890        return getMappingAsString((GameAction) (GA_FIRST_KARTACTION
00891                                                                              + playerIndex * KC_COUNT + ka));
00892 }
00893 // -----------------------------------------------------------------------------
00894 
00895 void
00896 UserConfig::unsetDuplicates (GameAction ga, Input &i)
00897 {
00898   for (int cga = GA_FIRST_KARTACTION; cga <= GA_LAST_KARTACTION; cga++)
00899   {
00900     if (cga != ga)
00901     {
00902       // If the input occurs in any other mapping
00903       // delete it properly from there.
00904       
00905       if (inputMap[cga].count
00906                 && inputMap[cga].inputs[0].type == i.type
00907           && inputMap[cga].inputs[0].id0 == i.id0
00908           && inputMap[cga].inputs[0].id1 == i.id1
00909           && inputMap[cga].inputs[0].id2 == i.id2)
00910       {
00911         // Delete it.
00912         inputMap[cga].inputs[0].type = IT_NONE;
00913       }
00914     }
00915   }
00916 }
00917 // -----------------------------------------------------------------------------
00918 void
00919 UserConfig::set(GameAction ga, Input i)
00920 {
00921   inputMap[ga].count = 1;
00922   inputMap[ga].inputs = new Input[1];
00923   inputMap[ga].inputs[0] = i;
00924 }
00925 // -----------------------------------------------------------------------------
00926 void
00927 UserConfig::set(GameAction ga, Input i0, Input i1)
00928 {
00929   inputMap[ga].count = 2;
00930   inputMap[ga].inputs = new Input[2];
00931   inputMap[ga].inputs[0] = i0;
00932   inputMap[ga].inputs[1] = i1;
00933 }
00934 // -----------------------------------------------------------------------------
00935 void
00936 UserConfig::set(GameAction ga, Input i0, Input i1, Input i2)
00937 {
00938   inputMap[ga].count = 3;
00939   inputMap[ga].inputs = new Input[3];
00940   inputMap[ga].inputs[0] = i0;
00941   inputMap[ga].inputs[1] = i1;
00942   inputMap[ga].inputs[2] = i2;
00943 }
00944 // -----------------------------------------------------------------------------
00945 void
00946 UserConfig::set(GameAction ga, Input i0, Input i1, Input i2, Input i3)
00947 {
00948   inputMap[ga].count = 4;
00949   inputMap[ga].inputs = new Input[4];
00950   inputMap[ga].inputs[0] = i0;
00951   inputMap[ga].inputs[1] = i1;
00952   inputMap[ga].inputs[2] = i2;
00953   inputMap[ga].inputs[3] = i3;
00954 }
00955 
00956 // -----------------------------------------------------------------------------
00957 void
00958 UserConfig::setInput(GameAction ga, Input &input)
00959 {
00960        // Removes the input from all mappings where it occurs.
00961        unsetDuplicates(ga, input);
00962 
00963        set(ga, input);
00964 }
00965 // -----------------------------------------------------------------------------
00966 void
00967 UserConfig::setInput(int playerIndex, KartAction ka, Input &input)
00968 {
00969        setInput((GameAction) (GA_FIRST_KARTACTION
00970                                              + playerIndex * KC_COUNT + ka),
00971                       input);
00972 }
00973 // -----------------------------------------------------------------------------
00974 void
00975 UserConfig::clearInput(int playerIndex, KartAction ka)
00976 {
00977        inputMap[(GameAction) (GA_FIRST_KARTACTION + playerIndex * KC_COUNT + ka)]
00978               .count = 0;
00979 }
00980 // -----------------------------------------------------------------------------
00981 ActionMap *
00982 UserConfig::newActionMap(const int from, const int to)
00983 {
00984        ActionMap *am = new ActionMap();
00985        
00986        for (int i = from; i <= to; i++)
00987        {
00988               const int count = inputMap[i].count;
00989               for (int j = 0;j < count; j++)
00990                      am->putEntry(inputMap[i].inputs[j], (GameAction) i);
00991        }
00992        
00993        return am;
00994 }
00995 // -----------------------------------------------------------------------------
00996 ActionMap *
00997 UserConfig::newMenuActionMap()
00998 {
00999        return newActionMap(GA_FIRST_MENU, GA_LAST_MENU);
01000 }
01001 // -----------------------------------------------------------------------------
01002 ActionMap *
01003 UserConfig::newIngameActionMap()
01004 {
01005        // This is rather unfriendly hack but work quite effective:
01006        // In order to prevent the input driver from handling input mappings
01007        // for human players which are not in the current game we select a subset
01008        // of the game actions by looking at the amount of players. The
01009        // race_manager instance is assumed to be available at this time because
01010        // this method is called immediately before the RaceGUI instance is created
01011        // (in MenuManager) and RaceGUI needs race_manager, too.
01012 
01013   // TODO: Reorder ingame GameAction values so that they start with
01014   // the fixed ones. This would allow simpler looking code here. 
01015 
01016        GameAction gaEnd = GA_NULL;
01017        
01018        switch (race_manager->getNumPlayers())
01019        {
01020               case 1:
01021                      gaEnd = GA_P1_LOOK_BACK; break;
01022               case 2:
01023                      gaEnd = GA_P2_LOOK_BACK; break;
01024               case 3:
01025                      gaEnd = GA_P3_LOOK_BACK; break;
01026               case 4:
01027                      gaEnd = GA_P4_LOOK_BACK; break;
01028        }
01029        
01030        ActionMap *am = newActionMap(GA_FIRST_INGAME, gaEnd);
01031 
01032        for (int i = GA_FIRST_INGAME_FIXED; i <= GA_LAST_INGAME_FIXED; i++)
01033        {
01034               const int count = inputMap[i].count;
01035               for (int j = 0;j < count; j++)
01036                      am->putEntry(inputMap[i].inputs[j], (GameAction) i);
01037        }
01038 
01039        return am;
01040 }
01041 // -----------------------------------------------------------------------------
01046 bool
01047 UserConfig::isFixedInput(InputType type, int id0, int id1, int id2)
01048 {
01049        for (int i = GA_FIRST_INGAME_FIXED; i <= GA_LAST_INGAME_FIXED; i++)
01050        {
01051               const int count = inputMap[i].count;
01052               for (int j = 0;j < count; j++)
01053                      if (inputMap[i].inputs[j].type == type
01054                             && inputMap[i].inputs[j].id0 == id0
01055                             && inputMap[i].inputs[j].id1 == id1
01056                             && inputMap[i].inputs[j].id2 == id2)
01057                             return true;
01058        }
01059        
01060        return false;
01061 }
01062 
01063 // -----------------------------------------------------------------------------
01064 
01065 void
01066 UserConfig::addStickConfig(StickConfig *sc)
01067 {
01068        m_stickconfigs.push_back(sc);
01069 }
01070 
01071 // -----------------------------------------------------------------------------
01072 const std::vector<UserConfig::StickConfig *> *
01073 UserConfig::getStickConfigs() const
01074 {
01075        return &m_stickconfigs;
01076 }
01077 
01078 UserConfig::StickConfig::StickConfig(string &newId)
01079 : id(newId)
01080 {
01081        // Nothing else to do.
01082 }
01083 
01084 /*EOF*/