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