1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2006-2015 Joerg Henrichs
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "race/highscore_manager.hpp"
20 
21 #include <stdexcept>
22 #include <fstream>
23 
24 #include "config/user_config.hpp"
25 #include "io/file_manager.hpp"
26 #include "io/utf_writer.hpp"
27 #include "race/race_manager.hpp"
28 #include "utils/constants.hpp"
29 #include "utils/log.hpp"
30 #include "utils/string_utils.hpp"
31 #include "utils/translation.hpp"
32 
33 HighscoreManager* highscore_manager=0;
34 const unsigned int HighscoreManager::CURRENT_HSCORE_FILE_VERSION = 4;
35 
HighscoreManager()36 HighscoreManager::HighscoreManager()
37 {
38     m_can_write=true;
39     setFilename();
40     loadHighscores();
41 }   // HighscoreManager
42 
43 // -----------------------------------------------------------------------------
~HighscoreManager()44 HighscoreManager::~HighscoreManager()
45 {
46     saveHighscores();
47     for(type_all_scores::iterator i  = m_all_scores.begin();
48                                   i != m_all_scores.end();  i++)
49         delete *i;
50 }   // ~HighscoreManager
51 
52 // -----------------------------------------------------------------------------
53 /** Determines the path to store the highscore file in
54  */
setFilename()55 void HighscoreManager::setFilename()
56 {
57     if ( getenv("SUPERTUXKART_HIGHSCOREDIR") != NULL )
58     {
59         m_filename = getenv("SUPERTUXKART_HIGHSCOREDIR")
60                    + std::string("/highscore.xml");
61     }
62     else
63     {
64         m_filename=file_manager->getUserConfigFile("highscore.xml");
65     }
66 
67     return;
68 }   // SetFilename
69 
70 // -----------------------------------------------------------------------------
loadHighscores()71 void HighscoreManager::loadHighscores()
72 {
73     XMLNode *root = NULL;
74     root = file_manager->createXMLTree(m_filename);
75     if(!root)
76     {
77         saveHighscores();
78         if(m_can_write)
79         {
80             Log::info("Highscore Manager", "New highscore file '%s' created.\n",
81                     m_filename.c_str());
82         }
83         delete root;
84         return;
85     }
86 
87     try
88     {
89         if(!root || root->getName()!="highscores")
90         {
91             if(root) delete root;
92             root = NULL;
93             throw std::runtime_error("No 'highscore' node found.");
94         }
95 
96         // check file version
97         int v;
98         if (!root->get("version", &v) || v<(int)CURRENT_HSCORE_FILE_VERSION)
99         {
100             Log::error("Highscore Manager", "Highscore file format too old, a new one will be created.\n");
101             irr::core::stringw warning =
102                 _("The highscore file was too old,\nall highscores have been erased.");
103             user_config->setWarning( warning );
104 
105             // since we haven't had the chance to load the current scores yet,
106             // calling Save() now will generate an empty file with the right format.
107             saveHighscores();
108             delete root;
109             root = NULL;
110             return;
111         }
112 
113         // read all entries one by one and store them in 'm_all_scores'
114         for(unsigned int i=0; i<root->getNumNodes(); i++)
115         {
116             const XMLNode *node = root->getNode(i);
117             Highscores *highscores;
118             try
119             {
120                 highscores = new Highscores(*node);
121             }
122             catch (std::logic_error& e)
123             {
124                 Log::error("Highscore Manager", "Invalid highscore entry will be skipped : %s\n", e.what());
125                 continue;
126             }
127             m_all_scores.push_back(highscores);
128         }   // next entry
129 
130         if(UserConfigParams::logMisc())
131             Log::error("Highscore Manager", "Highscores will be saved in '%s'.\n",
132                     m_filename.c_str());
133     }
134     catch(std::exception& err)
135     {
136         Log::error("Highscore Manager", "Error while parsing highscore file '%s':\n",
137                 m_filename.c_str());
138         Log::error("Highscore Manager", "%s", err.what());
139         Log::error("Highscore Manager", "\n");
140         Log::error("Highscore Manager", "No old highscores will be available.\n");
141     }
142     if(root)
143         delete root;
144 }   // loadHighscores
145 
146 // -----------------------------------------------------------------------------
saveHighscores()147 void HighscoreManager::saveHighscores()
148 {
149     // Print error message only once
150     if(!m_can_write) return;
151 
152     try
153     {
154         UTFWriter highscore_file(m_filename.c_str(), false);
155         highscore_file << "<?xml version=\"1.0\"?>\n";
156         highscore_file << "<highscores version=\"" << CURRENT_HSCORE_FILE_VERSION << "\">\n";
157 
158         for(unsigned int i=0; i<m_all_scores.size(); i++)
159         {
160             m_all_scores[i]->writeEntry(highscore_file);
161         }
162         highscore_file << "</highscores>\n";
163         highscore_file.close();
164     }
165     catch(std::exception &e)
166     {
167         Log::error("Highscore Manager","Problems saving highscores in '%s'\n", m_filename.c_str());
168         puts(e.what());
169         m_can_write=false;
170     }
171 
172 }   // saveHighscores
173 
174 // -----------------------------------------------------------------------------
175 /*
176  * Returns the high scores entry for a specific type of race.
177  * Creates one if none exists yet.
178  */
getHighscores(const Highscores::HighscoreType & highscore_type,int num_karts,const RaceManager::Difficulty difficulty,const std::string & trackName,const int number_of_laps,const bool reverse)179 Highscores* HighscoreManager::getHighscores(const Highscores::HighscoreType &highscore_type,
180                                             int num_karts,
181                                             const RaceManager::Difficulty difficulty,
182                                             const std::string &trackName,
183                                             const int number_of_laps,
184                                             const bool reverse)
185 {
186     Highscores *highscores = 0;
187 
188     // See if we already have a record for this type
189     for(type_all_scores::iterator i  = m_all_scores.begin();
190                                   i != m_all_scores.end();  i++)
191     {
192         if((*i)->matches(highscore_type, num_karts, difficulty, trackName,
193                          number_of_laps, reverse) )
194         {
195             // we found one entry for this kind of race, return it
196             return (*i);
197         }
198     }   // for i in m_all_scores
199 
200     // we don't have an entry for such a race currently. Create one.
201     highscores = new Highscores(highscore_type, num_karts, difficulty,
202                                 trackName, number_of_laps, reverse);
203     m_all_scores.push_back(highscores);
204     return highscores;
205 }   // getHighscores
206