1 /* 2 Bastet - tetris clone with embedded bastard block chooser 3 (c) 2005-2009 Federico Poloni <f.polonithirtyseven@sns.it> minus 37 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (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, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include "Config.hpp" 20 21 #include <boost/program_options.hpp> 22 #include <boost/foreach.hpp> 23 #include <boost/format.hpp> 24 #include <boost/algorithm/string.hpp> 25 #include <boost/assign.hpp> 26 #include <curses.h> 27 #include <cstdlib> 28 #include <fstream> 29 30 //DBG 31 #include <iostream> 32 33 using namespace std; 34 using namespace boost; 35 namespace po=boost::program_options; 36 37 namespace Bastet{ 38 const size_t HowManyHighScores=10; 39 const std::string RcFileName="/.bastetrc"; 40 const std::string LocalHighScoresFileName="/.bastetscores"; 41 const std::string GlobalHighScoresFileName="/var/games/bastet.scores2"; 42 Qualifies(int score)43 bool HighScores::Qualifies(int score){ 44 stable_sort(begin(),end()); 45 return begin()->Score < score; 46 } 47 InsertHighScore(int score,const std::string & scorer)48 int HighScores::InsertHighScore(int score, const std::string &scorer){ 49 if(!Qualifies(score)) return -1; 50 HighScore hs={score,scorer}; 51 insert(begin(),hs); //we insert at the beginning to resolve ties 52 stable_sort(begin(),end()); //the dumbest way to do it 53 erase(begin()); 54 iterator it=find(begin(),end(),hs); //the dumbest way to find the position 55 return end()-it+1; 56 } 57 58 Config config; //singleton instance 59 GetConfigFileName() const60 std::string Config::GetConfigFileName() const{ 61 return string(getenv("HOME"))+RcFileName; 62 } 63 64 class CannotOpenFile{}; 65 GetHighScoresFileName() const66 std::string Config::GetHighScoresFileName() const{ 67 static std::string result; //gets cached 68 if(!result.empty()) return result; 69 70 //tries the global one first and sees if it's writable 71 fstream ofs(GlobalHighScoresFileName.c_str()); 72 if(!ofs.fail()){ 73 result=GlobalHighScoresFileName; 74 } 75 ofs.close(); 76 77 //falls back to the user-specific file 78 string s=string(getenv("HOME"))+LocalHighScoresFileName; 79 if(result.empty()){ 80 cerr<<boost::str(boost::format("bastet: using a user-specific high scores file: %1%\nas the global high scores file %2% is not writable\n") % s % GlobalHighScoresFileName); 81 fstream ofs2(s.c_str()); 82 if(!ofs2.fail()){ 83 84 result=s; 85 } 86 ofs2.close(); 87 } 88 89 //tries to create the local high scores 90 if(result.empty()){ 91 ofstream ofs3(s.c_str()); 92 if(!ofs3.fail()){ 93 cerr<<boost::str(boost::format("bastet: creating a new user-specific high scores file %1%\n") % s); 94 result=s; 95 } 96 ofs3.close(); 97 } 98 if(result.empty()) 99 throw(CannotOpenFile()); 100 else return result; 101 } 102 Config()103 Config::Config(){ 104 po::options_description keyMappingOpts("Key mappings"); 105 keyMappingOpts.add_options() 106 ("Down",po::value<int>()->default_value(KEY_DOWN),"Down key") 107 ("Left",po::value<int>()->default_value(KEY_LEFT),"Left key") 108 ("Right",po::value<int>()->default_value(KEY_RIGHT),"Right key") 109 ("RotateCW",po::value<int>()->default_value(' '),"Clockwise turn key") 110 ("RotateCCW",po::value<int>()->default_value(KEY_UP),"Counterclockwise turn key") 111 ("Drop",po::value<int>()->default_value(KEY_ENTER),"Drop tetromino key") 112 ("Pause",po::value<int>()->default_value('p'),"Pause key") 113 ; 114 115 po::options_description highScoresOpts("High scores"); 116 boost::format scorer("Scorer%02d%02d"); 117 boost::format score("Score%02d%02d"); 118 for(int difficulty=0;difficulty<num_difficulties;++difficulty) 119 for(size_t i=0;i<HowManyHighScores;i++){ 120 highScoresOpts.add_options() 121 (str(scorer % difficulty % i).c_str(),po::value<string>()->default_value("No one played yet"),"Name of high scorer") 122 (str(score % difficulty % i).c_str(),po::value<int>()->default_value(0),"High score (points)") 123 ; 124 } 125 126 boost::program_options::variables_map _options; 127 boost::program_options::variables_map _highScores; 128 129 ifstream ifs(GetConfigFileName().c_str()); 130 po::store(po::parse_config_file(ifs,keyMappingOpts),_options); 131 132 _keys.Down=_options["Down"].as<int>(); 133 _keys.Left=_options["Left"].as<int>(); 134 _keys.Right=_options["Right"].as<int>(); 135 _keys.RotateCW=_options["RotateCW"].as<int>(); 136 _keys.RotateCCW=_options["RotateCCW"].as<int>(); 137 _keys.Drop=_options["Drop"].as<int>(); 138 _keys.Pause=_options["Pause"].as<int>(); 139 140 string s=GetHighScoresFileName(); 141 ifstream ifs2(s.c_str()); 142 po::store(po::parse_config_file(ifs2,highScoresOpts),_highScores); 143 144 for(int difficulty=0;difficulty<num_difficulties;difficulty++){ 145 for(size_t i=0;i<HowManyHighScores;++i) 146 _hs[difficulty].push_back((HighScore){_highScores[str(score % difficulty % i)].as<int>(), 147 _highScores[str(scorer % difficulty % i)].as<string>()}); 148 stable_sort(_hs[difficulty].begin(),_hs[difficulty].end()); //should not be needed but... 149 } 150 } 151 GetKeys()152 Keys *Config::GetKeys(){ 153 return &_keys; 154 } 155 GetHighScores(int difficulty)156 HighScores *Config::GetHighScores(int difficulty){ 157 assert(difficulty>=0 && difficulty<num_difficulties); 158 return &(_hs[difficulty]); 159 } 160 ~Config()161 Config::~Config(){ 162 /** 163 The config and highscore files are written down at each game, even if it is not needed, but who cares for now 164 */ 165 boost::program_options::variables_map _options; 166 167 _options.insert(make_pair("Down",po::variable_value(_keys.Down,false))); 168 _options.insert(make_pair("Left",po::variable_value(_keys.Left,false))); 169 _options.insert(make_pair("Right",po::variable_value(_keys.Right,false))); 170 _options.insert(make_pair("RotateCW",po::variable_value(_keys.RotateCW,false))); 171 _options.insert(make_pair("RotateCCW",po::variable_value(_keys.RotateCCW,false))); 172 _options.insert(make_pair("Drop",po::variable_value(_keys.Drop,false))); 173 _options.insert(make_pair("Pause",po::variable_value(_keys.Pause,false))); 174 175 ofstream ofs(GetConfigFileName().c_str()); 176 ofs<<"# Automatically regenerated by the program at each run, edit at your own risk\n"; 177 BOOST_FOREACH(const po::variables_map::value_type &pair, _options){ 178 ofs<<pair.first<<" = "<<pair.second.as<int>()<<"\n"; 179 } 180 181 ofstream ofs2(GetHighScoresFileName().c_str()); 182 ofs2<<"# Do not edit this file, Bastet sees you\n"; 183 184 boost::format scorer("Scorer%02d%02d"); 185 boost::format score("Score%02d%02d"); 186 187 for(int difficulty=0;difficulty<num_difficulties;++difficulty){ 188 int i=0; 189 BOOST_FOREACH(const HighScore &hs, _hs[difficulty]){ 190 ofs2<<str(scorer % difficulty % i) << " = \"" << hs.Scorer << "\"\n"; 191 ofs2<<str(score % difficulty % i) << " = " << hs.Score <<"\n"; 192 i++; 193 } 194 } 195 } 196 } 197