game.cpp

Go to the documentation of this file.
00001 /* $Id: game.cpp 26800 2008-05-23 18:25:20Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #include "global.hpp"
00016 
00017 #include "SDL.h"
00018 #include "SDL_mixer.h"
00019 
00020 #include "about.hpp"
00021 #include "config.hpp"
00022 #include "construct_dialog.hpp"
00023 #include "cursor.hpp"
00024 #include "dialogs.hpp"
00025 #include "game_display.hpp"
00026 #include "filesystem.hpp"
00027 #include "font.hpp"
00028 #include "game_config.hpp"
00029 #include "game_errors.hpp"
00030 #include "gamestatus.hpp"
00031 #include "gettext.hpp"
00032 #include "gui/dialogs/addon_connect.hpp"
00033 #include "gui/dialogs/language_selection.hpp"
00034 #include "gui/dialogs/mp_method_selection.hpp"
00035 #include "gui/widgets/button.hpp"
00036 #include "help.hpp"
00037 #include "hotkeys.hpp"
00038 #include "intro.hpp"
00039 #include "language.hpp"
00040 #include "loadscreen.hpp"
00041 #include "log.hpp"
00042 #include "widgets/menu.hpp"
00043 #include "marked-up_text.hpp"
00044 #include "multiplayer.hpp"
00045 #include "network.hpp"
00046 #include "playcampaign.hpp"
00047 #include "preferences_display.hpp"
00048 #include "publish_campaign.hpp"
00049 #include "replay.hpp"
00050 #include "sound.hpp"
00051 #include "statistics.hpp"
00052 #include "thread.hpp"
00053 #include "titlescreen.hpp"
00054 #include "util.hpp"
00055 #include "upload_log.hpp"
00056 #include "wml_exception.hpp"
00057 #include "wml_separators.hpp"
00058 #include "serialization/binary_or_text.hpp"
00059 #include "serialization/binary_wml.hpp"
00060 #include "serialization/parser.hpp"
00061 #include "serialization/preprocessor.hpp"
00062 #include "serialization/string_utils.hpp"
00063 #include "sha1.hpp"
00064 
00065 #ifdef HAVE_PYTHON
00066 #include "ai_python.hpp"
00067 #endif
00068 
00069 #include "wesconfig.h"
00070 
00071 #include <clocale>
00072 #include <cmath>
00073 #include <cstdlib>
00074 #include <ctime>
00075 #include <fstream>
00076 #include <iostream>
00077 #include <iterator>
00078 #include <sstream>
00079 #include <string>
00080 
00081 #include <boost/iostreams/copy.hpp>
00082 #include <boost/iostreams/filtering_streambuf.hpp>
00083 #include <boost/iostreams/filter/gzip.hpp>
00084 
00085 #define ERR_CONFIG LOG_STREAM(err, config)
00086 #define WRN_CONFIG LOG_STREAM(warn, config)
00087 #define DBG_CONFIG LOG_STREAM(debug, config)
00088 #define LOG_CONFIG LOG_STREAM(info, config)
00089 #define LOG_GENERAL LOG_STREAM(info, general)
00090 #define DBG_GENERAL LOG_STREAM(debug, general)
00091 #define ERR_NET LOG_STREAM(err, network)
00092 #define LOG_NET LOG_STREAM(info, network)
00093 #define ERR_FS LOG_STREAM(err, filesystem)
00094 
00095 static bool less_campaigns_rank(const config* a, const config* b) {
00096     return lexical_cast_default<int>((*a)["rank"],1000) <
00097            lexical_cast_default<int>((*b)["rank"],1000);
00098 }
00099 
00100 namespace {
00101 
00102 static bool new_widgets = false;
00103 
00104 class game_controller
00105 {
00106 public:
00107     game_controller(int argc, char** argv);
00108     ~game_controller();
00109 
00110     game_display& disp();
00111 
00112     bool init_video();
00113     bool init_config();
00114     bool init_language();
00115     bool play_test();
00116     bool play_multiplayer_mode();
00117 
00118     void reset_game_cfg();
00119     void reset_defines_map();
00120     void reload_changed_game_config();
00121     void read_configs(std::string&);
00122 
00123     bool is_loading() const;
00124     bool load_game();
00125     void set_tutorial();
00126 
00127     bool new_campaign();
00128     bool goto_campaign();
00129     bool goto_multiplayer();
00130     bool play_multiplayer();
00131     void manage_addons();
00132     void download_campaigns(std::string host);
00133     bool change_language();
00134 
00135     void show_help();
00136     void show_preferences();
00137     void show_upload_begging();
00138 
00139     enum RELOAD_GAME_DATA { RELOAD_DATA, NO_RELOAD_DATA };
00140     void play_game(RELOAD_GAME_DATA reload=RELOAD_DATA);
00141     void play_replay();
00142     const config& game_config(){return game_config_;};
00143 
00144 private:
00145     game_controller(const game_controller&);
00146     void operator=(const game_controller&);
00147 
00148     void read_game_cfg(bool use_cache);
00149     void refresh_game_cfg(bool reset_translations=false);
00150     void set_unit_data();
00151 
00152     void upload_campaign(const std::string& campaign, network::connection sock);
00153     void delete_campaign(const std::string& campaign, network::connection sock);
00154     void remove_campaign(const std::string& campaign);
00155 
00156     const int argc_;
00157     int arg_;
00158     const char* const * const argv_;
00159 
00160     //this should get destroyed *after* the video, since we want
00161     //to clean up threads after the display disappears.
00162     const threading::manager thread_manager;
00163 
00164     CVideo video_;
00165 
00166     const font::manager font_manager_;
00167     const preferences::manager prefs_manager_;
00168     const image::manager image_manager_;
00169     const events::event_context main_event_context_;
00170     const hotkey::manager hotkey_manager_;
00171     const upload_log::manager upload_log_manager_;
00172     sound::music_thinker music_thinker_;
00173     resize_monitor resize_monitor_;
00174     binary_paths_manager paths_manager_;
00175 
00176     std::string test_scenario_;
00177 
00178     bool test_mode_, multiplayer_mode_, no_gui_;
00179     bool use_caching_;
00180     bool force_valid_cache_;
00181     int force_bpp_;
00182 
00183     config game_config_;
00184 
00185     util::scoped_ptr<game_display> disp_;
00186 
00187     game_state state_;
00188 
00189     std::string loaded_game_;
00190     bool loaded_game_show_replay_;
00191     bool loaded_game_cancel_orders_;
00192 
00193     preproc_map defines_map_, old_defines_map_;
00194 
00195     std::string multiplayer_server_;
00196     bool jump_to_campaign_, jump_to_multiplayer_;
00197 };
00198 
00199 game_controller::game_controller(int argc, char** argv)
00200    : argc_(argc), arg_(1), argv_(argv), thread_manager(),
00201      test_scenario_("test"), test_mode_(false), multiplayer_mode_(false),
00202      no_gui_(false), use_caching_(true), force_valid_cache_(false),
00203      force_bpp_(-1), disp_(NULL), loaded_game_show_replay_(false),
00204      loaded_game_cancel_orders_(false),
00205      jump_to_campaign_(false), jump_to_multiplayer_(false)
00206 {
00207     bool no_sound = false;
00208     for(arg_ = 1; arg_ != argc_; ++arg_) {
00209         const std::string val(argv_[arg_]);
00210         if(val.empty()) {
00211             continue;
00212         }
00213 
00214         if(val == "--fps") {
00215             preferences::set_show_fps(true);
00216         } else if(val == "--nocache") {
00217             use_caching_ = false;
00218         } else if(val == "--max-fps") {
00219             if(arg_+1 != argc_) {
00220                 ++arg_;
00221                 int fps = lexical_cast_default<int>(argv_[arg_], 50);
00222                 fps = minimum<int>(fps, 1000);
00223                 fps = maximum<int>(fps, 1);
00224                 fps = 1000 / fps;
00225                 // increase the delay to avoid going above the maximum
00226                 if(1000 % fps != 0) {
00227                     ++fps;
00228                 }
00229                 preferences::set_draw_delay(fps);
00230             }
00231         } else if(val == "--validcache") {
00232             force_valid_cache_ = true;
00233         } else if(val == "--resolution" || val == "-r") {
00234             if(arg_+1 != argc_) {
00235                 ++arg_;
00236                 const std::string val(argv_[arg_]);
00237                 const std::vector<std::string> res = utils::split(val, 'x');
00238                 if(res.size() == 2) {
00239                     const int xres = lexical_cast_default<int>(res.front());
00240                     const int yres = lexical_cast_default<int>(res.back());
00241                     if(xres > 0 && yres > 0) {
00242                         const std::pair<int,int> resolution(xres,yres);
00243                         preferences::set_resolution(resolution);
00244                     }
00245                 }
00246             }
00247         } else if(val == "--bpp") {
00248             if(arg_+1 != argc_) {
00249                 ++arg_;
00250                 force_bpp_ = lexical_cast_default<int>(argv_[arg_],-1);
00251             }
00252         } else if(val == "--load" || val == "-l") {
00253             if(arg_+1 != argc_) {
00254                 ++arg_;
00255                 loaded_game_ = argv_[arg_];
00256             }
00257         } else if(val == "--with-replay") {
00258             loaded_game_show_replay_ = true;
00259 
00260         } else if(val == "--nogui") {
00261             no_gui_ = true;
00262             no_sound = true;
00263             preferences::disable_preferences_save();
00264         } else if(val == "--windowed" || val == "-w") {
00265             preferences::set_fullscreen(false);
00266         } else if(val == "--fullscreen" || val == "-f") {
00267             preferences::set_fullscreen(true);
00268 
00269         } else if(val == "--campaign" || val == "-c") {
00270             jump_to_campaign_ = true;
00271 
00272         } else if(val == "--server" || val == "-s"){
00273             jump_to_multiplayer_ = true;
00274             //Do we have any server specified ?
00275             if(argc_ > arg_+1){
00276                 multiplayer_server_ = argv_[arg_+1];
00277                 ++arg_;
00278             //Pick the first server in config
00279             }else{
00280                 if(game_config::server_list.size() > 0)
00281                     multiplayer_server_ = preferences::network_host();
00282                 else
00283                     multiplayer_server_ = "";
00284             }
00285 
00286         } else if(val == "--multiplayer" || val == "-m") {
00287             multiplayer_mode_ = true;
00288             break; //parse the rest of the arguments when we set up the game
00289         } else if(val == "--test" || val == "-t") {
00290             test_mode_ = true;
00291             // If we have -t foo it's ambiguous whether it foo is the parameter
00292             // for Wesnoth or the start directory so we assume it's the starting
00293             // directory.
00294             if(arg_ + 2 < argc_ && argv_[arg_ + 1][0] != '-') {
00295                 ++arg_;
00296                 test_scenario_ = argv_[arg_];
00297             }
00298         } else if(val == "--debug" || val == "-d") {
00299             game_config::debug = true;
00300             game_config::mp_debug = true;
00301         } else if(val == "--no-delay") {
00302             game_config::no_delay = true;
00303         } else if (val.substr(0, 6) == "--log-") {
00304         } else if(val == "--nosound") {
00305             no_sound = true;
00306         } else if(val == "--new-widgets") {
00307             // This is a hidden option to enable the new widget toolkit.
00308             new_widgets = true;
00309         } else if(val[0] == '-') {
00310             std::cerr << "unknown option: " << val << std::endl;
00311             throw config::error("unknown option");
00312         } else {
00313 
00314           std::cerr << "Setting path using " << val << std::endl;
00315             if(val[0] == '/') {
00316                 game_config::path = val;
00317             } else {
00318                 game_config::path = get_cwd() + '/' + val;
00319             }
00320 
00321             if(!is_directory(game_config::path)) {
00322                 std::cerr << "Could not find directory '" << game_config::path << "'\n";
00323                 throw config::error("directory not found");
00324             }
00325 
00326         }
00327     }
00328     std::cerr << "Data at '" << game_config::path << "'\n";
00329 
00330     // disable sound in nosound mode, or when sound engine failed to initialize
00331     if (no_sound || ((preferences::sound_on() || preferences::music_on() ||
00332                       preferences::turn_bell() || preferences::UI_sound_on()) &&
00333                      !sound::init_sound())) {
00334         preferences::set_sound(false);
00335         preferences::set_music(false);
00336         preferences::set_turn_bell(false);
00337         preferences::set_UI_sound(false);
00338     }
00339 }
00340 
00341 game_display& game_controller::disp()
00342 {
00343     if(disp_.get() == NULL) {
00344 
00345         if(get_video_surface() == NULL) {
00346             throw CVideo::error();
00347         }
00348 
00349         static unit_map dummy_umap;
00350         static config dummy_cfg;
00351         static gamemap dummy_map(dummy_cfg, "");
00352         static gamestatus dummy_status(dummy_cfg, 0);
00353         static std::vector<team> dummy_teams;
00354         disp_.assign(new game_display(dummy_umap, video_, dummy_map, dummy_status,
00355             dummy_teams, dummy_cfg, dummy_cfg, dummy_cfg));
00356     }
00357 
00358     return *disp_.get();
00359 }
00360 
00361 bool game_controller::init_video()
00362 {
00363     if(no_gui_) {
00364         if(!multiplayer_mode_) {
00365             std::cerr << "--nogui flag is only valid with --multiplayer flag\n";
00366             return false;
00367         }
00368         video_.make_fake();
00369         game_config::no_delay = true;
00370         return true;
00371     }
00372 
00373     image::set_wm_icon();
00374 
00375     int video_flags = preferences::fullscreen() ? FULL_SCREEN : 0;
00376 
00377     std::pair<int,int> resolution = preferences::resolution();
00378 
00379     int DefaultBPP = 24;
00380     const SDL_VideoInfo* const video_info = SDL_GetVideoInfo();
00381     if(video_info != NULL && video_info->vfmt != NULL) {
00382         DefaultBPP = video_info->vfmt->BitsPerPixel;
00383     }
00384 
00385     std::cerr << "Checking video mode: " << resolution.first
00386           << "x" << resolution.second << "x" << DefaultBPP << "...\n";
00387     int bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00388 
00389     if(bpp == 0) {
00390         //Video mode not supported, maybe from bad prefs.
00391         std::cerr << "Video mode " << resolution.first
00392                   << "x" << resolution.second << "x" << DefaultBPP << " "
00393                   << "is not supported - attempting 1024x768x" << DefaultBPP << "...\n";
00394 
00395         //Attempt 1024x768.
00396         resolution.first = 1024;
00397         resolution.second = 768;
00398 
00399         bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00400 
00401         if(bpp == 0) {
00402             std::cerr << "1024x768x" << DefaultBPP << " not available - attempting 800x600x" << DefaultBPP << "...\n";
00403 
00404             resolution.first = 800;
00405             resolution.second = 600;
00406 
00407             bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00408         }
00409 
00410 #ifdef USE_TINY_GUI
00411         if(bpp == 0) {
00412             std::cerr << "800x600x" << DefaultBPP << " not available - attempting 640x480x" << DefaultBPP << "...\n";
00413 
00414             resolution.first = 640;
00415             resolution.second = 480;
00416 
00417             bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00418         }
00419 
00420         if(bpp == 0) {
00421             std::cerr << "640x480x" << DefaultBPP << " not available - attempting 320x240x" << DefaultBPP << "...\n";
00422 
00423             resolution.first = 320;
00424             resolution.second = 240;
00425 
00426             bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00427         }
00428 #endif
00429 
00430 #ifdef USE_SMALL_GUI
00431         if(bpp == 0) {
00432             std::cerr << "800x600x" << DefaultBPP << " not available - attempting 800x480x" << DefaultBPP << "...\n";
00433 
00434             resolution.first = 800;
00435             resolution.second = 480;
00436 
00437             bpp = video_.modePossible(resolution.first,resolution.second,DefaultBPP,video_flags);
00438         }
00439 #endif
00440 
00441         if(bpp == 0) {
00442             //couldn't do 1024x768 or 800x600
00443 
00444             std::cerr << "The required video mode, " << resolution.first
00445                       << "x" << resolution.second << "x" << DefaultBPP << " "
00446                       << "is not supported\n";
00447 
00448             if((video_flags&FULL_SCREEN) != 0) {
00449                 std::cerr << "Try running the program with the --windowed option "
00450                           << "using a " << DefaultBPP << "bpp X windows setting\n";
00451             }
00452 
00453             if((video_flags&FULL_SCREEN) == 0) {
00454                 std::cerr << "Try running with the --fullscreen option\n";
00455             }
00456 
00457             return false;
00458         }
00459     }
00460 
00461     if(force_bpp_ > 0) {
00462         bpp = force_bpp_;
00463     }
00464 
00465     std::cerr << "setting mode to " << resolution.first << "x" << resolution.second << "x" << bpp << "\n";
00466     const int res = video_.setMode(resolution.first,resolution.second,bpp,video_flags);
00467     video_.setBpp(bpp);
00468     if(res == 0) {
00469         std::cerr << "required video mode, " << resolution.first << "x"
00470                   << resolution.second << "x" << bpp << " is not supported\n";
00471         return false;
00472     }
00473 
00474     return true;
00475 }
00476 
00477 bool game_controller::init_config()
00478 {
00479     //Resets old_defines_map_, to force refresh_game_cfg to reload
00480     //everything.
00481     old_defines_map_.clear();
00482     reset_game_cfg();
00483     refresh_game_cfg();
00484 
00485     game_config::load_config(game_config_.child("game_config"));
00486 
00487     hotkey::load_hotkeys(game_config_);
00488     paths_manager_.set_paths(game_config_);
00489 	::init_textdomains(game_config_);
00490     about::set_about(game_config_);
00491 
00492     return true;
00493 }
00494 
00495 bool game_controller::init_language()
00496 {
00497     if(!::load_language_list())
00498         return false;
00499 
00500     if (!::set_language(get_locale()))
00501         return false;
00502 
00503     if(!no_gui_) {
00504         std::string wm_title_string = _("The Battle for Wesnoth");
00505         wm_title_string += " - " + game_config::revision;
00506         SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
00507     }
00508 
00509     hotkey::load_descriptions();
00510 
00511     return true;
00512 }
00513 
00514 bool game_controller::play_test()
00515 {
00516     static bool first_time = true;
00517 
00518     if(test_mode_ == false) {
00519         return true;
00520     }
00521     if(!first_time)
00522         return false;
00523 
00524     first_time = false;
00525 
00526     state_.campaign_type = "test";
00527     state_.scenario = test_scenario_;
00528 
00529     try {
00530         refresh_game_cfg();
00531     } catch(config::error&) {
00532         reset_game_cfg();
00533         refresh_game_cfg();
00534         return false;
00535     }
00536 
00537     try {
00538         upload_log nolog(false);
00539 		::play_game(disp(),state_,game_config_,nolog);
00540     } catch(game::load_game_exception& e) {
00541         loaded_game_ = e.game;
00542         loaded_game_show_replay_ = e.show_replay;
00543         loaded_game_cancel_orders_ = e.cancel_orders;
00544         test_mode_ = false;
00545         return true;
00546     }
00547 
00548     return false;
00549 }
00550 
00551 bool game_controller::play_multiplayer_mode()
00552 {
00553     state_ = game_state();
00554 
00555     if(!multiplayer_mode_) {
00556         return true;
00557     }
00558 
00559     std::string era = "era_default";
00560     std::string scenario = "multiplayer_The_Freelands";
00561     std::map<int,std::string> side_types, side_controllers, side_algorithms;
00562     std::map<int,string_map> side_parameters;
00563     std::string turns = "50";
00564 
00565     size_t sides_counted = 0;
00566 
00567     for(++arg_; arg_ < argc_; ++arg_) {
00568         const std::string val(argv_[arg_]);
00569         if(val.empty()) {
00570             continue;
00571         }
00572 
00573         std::vector<std::string> name_value = utils::split(val, '=');
00574         if(name_value.size() > 2) {
00575             std::cerr << "invalid argument '" << val << "'\n";
00576             return false;
00577         } else if(name_value.size() == 2) {
00578             const std::string name = name_value.front();
00579             const std::string value = name_value.back();
00580 
00581             const std::string name_head = name.substr(0,name.size()-1);
00582             const char name_tail = name[name.size()-1];
00583             const bool last_digit = isdigit(name_tail) ? true:false;
00584             const size_t side = name_tail - '0';
00585 
00586             if(last_digit && side > sides_counted) {
00587                 std::cerr << "counted sides: " << side << "\n";
00588                 sides_counted = side;
00589             }
00590 
00591             if(name == "--scenario") {
00592                 scenario = value;
00593             } else if(name == "--turns") {
00594                 turns = value;
00595             } else if(name == "--era") {
00596                 era = value;
00597             } else if(last_digit && name_head == "--controller") {
00598                 side_controllers[side] = value;
00599             } else if(last_digit && name_head == "--algorithm") {
00600                 side_algorithms[side] = value;
00601             } else if(last_digit && name_head == "--side") {
00602                 side_types[side] = value;
00603             } else if(last_digit && name_head == "--parm") {
00604                 const std::vector<std::string> name_value = utils::split(value, ':');
00605                 if(name_value.size() != 2) {
00606                     std::cerr << "argument to '" << name << "' must be in the format name:value\n";
00607                     return false;
00608                 }
00609 
00610                 side_parameters[side][name_value.front()] = name_value.back();
00611             } else {
00612                 std::cerr << "unrecognized option: '" << name << "'\n";
00613                 return false;
00614             }
00615         } else {
00616             if (val == "--exit-at-end") {
00617                 game_config::exit_at_end = true;
00618             }
00619         }
00620     }
00621 
00622     const config* const lvl = game_config_.find_child("multiplayer","id",scenario);
00623     if(lvl == NULL) {
00624         std::cerr << "Could not find scenario '" << scenario << "'\n";
00625         return false;
00626     }
00627 
00628     state_.campaign_type = "multiplayer";
00629     state_.scenario = "";
00630     state_.snapshot = config();
00631 
00632     config level = *lvl;
00633     std::vector<config*> story;
00634 
00635     const config* const era_cfg = game_config_.find_child("era","id",era);
00636     if(era_cfg == NULL) {
00637         std::cerr << "Could not find era '" << era << "'\n";
00638         return false;
00639     }
00640 
00641     level["turns"] = turns;
00642 
00643     const config* const side = era_cfg->child("multiplayer_side");
00644     if(side == NULL) {
00645         std::cerr << "Could not find multiplayer side\n";
00646         return false;
00647     }
00648 
00649     while(level.get_children("side").size() < sides_counted) {
00650         std::cerr << "now adding side...\n";
00651         level.add_child("side");
00652     }
00653 
00654     int side_num = 1;
00655     for(config::child_itors itors = level.child_range("side"); itors.first != itors.second; ++itors.first, ++side_num) {
00656         std::map<int,std::string>::const_iterator type = side_types.find(side_num),
00657                                                   controller = side_controllers.find(side_num),
00658                                                   algorithm = side_algorithms.find(side_num);
00659 
00660         const config* side = type == side_types.end() ?
00661             era_cfg->find_child("multiplayer_side", "random_faction", "yes") :
00662             era_cfg->find_child("multiplayer_side", "id", type->second);
00663 
00664         if (side == NULL) {
00665             std::string side_name = (type == side_types.end() ? "default" : type->second);
00666             std::cerr << "Could not find side '" << side_name << "' for side " << side_num << "\n";
00667             return false;
00668         }
00669 
00670         if (utils::string_bool((*side)["random_faction"])) {
00671             const config::child_list& factions = era_cfg->get_children("multiplayer_side");
00672             std::vector<std::string> faction_choices, faction_excepts;
00673             faction_choices = utils::split((*side)["choices"]);
00674             if(faction_choices.size() == 1 && faction_choices.front() == "") {
00675                 faction_choices.clear();
00676             }
00677             faction_excepts = utils::split((*side)["except"]);;
00678             if(faction_excepts.size() == 1 && faction_excepts.front() == "") {
00679                 faction_excepts.clear();
00680             }
00681             for(unsigned int i = 0, j = 0; i < factions.size(); ++i) {
00682                 if (utils::string_bool((*factions[i])["random_faction"]) != true) {
00683                     const std::string& faction_id = (*factions[i])["id"];
00684                     if (
00685                         !faction_choices.empty() &&
00686                         std::find(faction_choices.begin(),faction_choices.end(),faction_id) == faction_choices.end()
00687                     )
00688                         continue;
00689                     if (
00690                         !faction_excepts.empty() &&
00691                         std::find(faction_excepts.begin(),faction_excepts.end(),faction_id) != faction_excepts.end()
00692                     )
00693                         continue;
00694                     j++;
00695                     if (rand()%j == 0) {
00696                         side = factions[i];
00697                     }
00698                 }
00699             }
00700             if (utils::string_bool((*side)["random_faction"], false) == true) {
00701                 std::string side_name = (type == side_types.end() ? "default" : type->second);
00702                 std::cerr << "Could not find any non-random faction for side " << side_num << "\n";
00703                 return false;
00704             }
00705         }
00706 
00707         char buf[20];
00708         snprintf(buf,sizeof(buf),"%d",side_num);
00709         (*itors.first)->values["side"] = buf;
00710 
00711         (*itors.first)->values["canrecruit"] = "yes";
00712 
00713         (*itors.first)->append(*side);
00714 
00715         if(controller != side_controllers.end()) {
00716             (*itors.first)->values["controller"] = controller->second;
00717         }
00718 
00719         if(algorithm != side_algorithms.end()) {
00720             (*itors.first)->values["ai_algorithm"] = algorithm->second;
00721         }
00722 
00723         config& ai_params = (*itors.first)->add_child("ai");
00724 
00725         //now add in any arbitrary parameters given to the side
00726         for(string_map::const_iterator j = side_parameters[side_num].begin(); j != side_parameters[side_num].end(); ++j) {
00727             (*itors.first)->values[j->first] = j->second;
00728             ai_params[j->first] = j->second;
00729         }
00730     }
00731 
00732     try {
00733         upload_log nolog(false);
00734         state_.snapshot = level;
00735 		::play_game(disp(),state_,game_config_,nolog);
00736     } catch(game::error& e) {
00737         std::cerr << "caught error: '" << e.message << "'\n";
00738     } catch(game::load_game_exception& e) {
00739         //the user's trying to load a game, so go into the normal title screen loop and load one
00740         loaded_game_ = e.game;
00741         loaded_game_show_replay_ = e.show_replay;
00742         loaded_game_cancel_orders_ = e.cancel_orders;
00743         return true;
00744     } catch(twml_exception& e) {
00745         e.show(disp());
00746         return false;
00747     } catch(...) {
00748         std::cerr << "caught unknown error playing level...\n";
00749     }
00750 
00751     return false;
00752 }
00753 
00754 bool game_controller::is_loading() const
00755 {
00756     return loaded_game_.empty() == false;
00757 }
00758 
00759 bool game_controller::load_game()
00760 {
00761     state_ = game_state();
00762 
00763     bool show_replay = loaded_game_show_replay_;
00764     bool cancel_orders = loaded_game_cancel_orders_;
00765 
00766     const std::string game = loaded_game_.empty() ? dialogs::load_game_dialog(disp(),game_config_,&show_replay,&cancel_orders) : loaded_game_;
00767 
00768     loaded_game_ = "";
00769 
00770     if(game == "") {
00771         return false;
00772     }
00773 
00774     try {
00775         //to load a save file, we first load the file in, then we re-parse game
00776         //data with the save's #defines, and then we finally parse the save file,
00777         //with the game data ready to go.
00778 
00779         config cfg;
00780         std::string error_log;
00781         read_save_file(game,cfg,&error_log);
00782         if(!error_log.empty()) {
00783             gui::show_error_message(disp(),
00784                     _("Warning: The file you have tried to load is corrupt. Loading anyway.\n") +
00785                     error_log);
00786         }
00787 
00788         reset_defines_map();
00789         defines_map_[cfg["difficulty"]] = preproc_define();
00790 
00791         if(defines_map_.count("NORMAL")) {
00792             defines_map_["MEDIUM"] = preproc_define();
00793         }
00794 
00795         const std::string& campaign_define = cfg["campaign_define"];
00796         if(campaign_define.empty() == false) {
00797             defines_map_[campaign_define] = preproc_define();
00798         }
00799         if (campaign_define.empty() && (cfg["campaign_type"] == "multiplayer")){
00800             defines_map_["MULTIPLAYER"] = preproc_define();
00801         }
00802 
00803         const std::vector<std::string> campaign_xtra_defines = utils::split(cfg["campaign_extra_defines"]);
00804 
00805         for(std::vector<std::string>::const_iterator i = campaign_xtra_defines.begin(); i != campaign_xtra_defines.end(); ++i) {
00806             defines_map_[*i] = preproc_define();
00807         }
00808 
00809         try {
00810             refresh_game_cfg();
00811         } catch(config::error&) {
00812             reset_game_cfg();
00813             refresh_game_cfg();
00814             return false;
00815         }
00816 
00817         const std::string version = cfg["version"];
00818         if(version != game_config::version) {
00819             // do not load if too old, if either the savegame or the current game
00820             // has the version 'test' allow loading
00821             if(version < game_config::min_savegame_version &&
00822                     game_config::test_version.full != version &&
00823                     game_config::test_version.full != game_config::version) {
00824 
00825                 /* GCC-3.3 needs a temp var otherwise compilation fails */
00826                 gui::message_dialog dlg(disp(), "", _("This save is from a version too old to be loaded."));
00827                 dlg.show();
00828                 return false;
00829             }
00830 
00831             const int res = gui::dialog(disp(),"",
00832                                   _("This save is from a different version of the game. Do you want to try to load it?"),
00833                                   gui::YES_NO).show();
00834             if(res == 1) {
00835                 return false;
00836             }
00837         }
00838 
00839         state_ = game_state(cfg, show_replay);
00840 
00841         // Get the status of the random in the snapshot.
00842         // For a replay we need to restore the start only, the replaying gets at
00843         // proper location.
00844         // For normal loading also restore the call count.
00845         const int seed = lexical_cast_default<int>
00846             (cfg["random_seed"], 42);
00847         const unsigned calls = show_replay ? 0 :
00848             lexical_cast_default<unsigned> (state_.snapshot["random_calls"]);
00849         state_.rng().seed_random(seed, calls);
00850 
00851     } catch(game::error& e) {
00852         gui::show_error_message(disp(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
00853         return false;
00854     } catch(config::error& e) {
00855         gui::show_error_message(disp(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
00856         return false;
00857     } catch(io_exception&) {
00858         gui::show_error_message(disp(), _("File I/O Error while reading the game"));
00859         return false;
00860     } catch(twml_exception& e) {
00861         e.show(disp());
00862         return false;
00863     }
00864     recorder = replay(state_.replay_data);
00865     recorder.start_replay();
00866     recorder.set_skip(false);
00867 
00868     LOG_CONFIG << "has snapshot: " << (state_.snapshot.child("side") ? "yes" : "no") << "\n";
00869 
00870     if(state_.snapshot.child("side") == NULL) {
00871         // No snapshot; this is a start-of-scenario
00872         if (show_replay) {
00873             // There won't be any turns to replay, but the
00874             // user gets to watch the intro sequence again ...
00875             LOG_CONFIG << "replaying (start of scenario)\n";
00876         } else {
00877             LOG_CONFIG << "skipping...\n";
00878             recorder.set_skip(false);
00879         }
00880     } else {
00881         // We have a snapshot. But does the user want to see a replay?
00882         if(show_replay) {
00883             statistics::clear_current_scenario();
00884             LOG_CONFIG << "replaying (snapshot)\n";
00885         } else {
00886             LOG_CONFIG << "setting replay to end...\n";
00887             recorder.set_to_end();
00888             if(!recorder.at_end()) {
00889                 WRN_CONFIG << "recorder is not at the end!!!\n";
00890             }
00891         }
00892     }
00893 
00894     if(state_.campaign_type == "tutorial") {
00895         defines_map_["TUTORIAL"] = preproc_define();
00896     } else if(state_.campaign_type == "multiplayer") {
00897         for(config::child_itors sides = state_.snapshot.child_range("side");
00898             sides.first != sides.second; ++sides.first) {
00899             if((**sides.first)["controller"] == "network")
00900                 (**sides.first)["controller"] = "human";
00901         }
00902     }
00903 
00904     if (cancel_orders) {
00905         for(config::child_itors sides = state_.snapshot.child_range("side");
00906                 sides.first != sides.second; ++sides.first) {
00907                 if((**sides.first)["controller"] == "human") {
00908                     for (config::child_itors units = (**sides.first).child_range("unit");
00909                             units.first != units.second; ++units.first) {
00910                         (**units.first)["goto_x"] = "-999";
00911                         (**units.first)["goto_y"] = "-999";
00912                     }
00913                 }
00914         }
00915     }
00916 
00917     return true;
00918 }
00919 
00920 void game_controller::set_tutorial()
00921 {
00922     state_ = game_state();
00923     state_.campaign_type = "tutorial";
00924     state_.scenario = "tutorial";
00925     state_.campaign_define = "TUTORIAL";
00926     reset_defines_map();
00927     defines_map_["TUTORIAL"] = preproc_define();
00928 
00929 }
00930 
00931 bool game_controller::new_campaign()
00932 {
00933     state_ = game_state();
00934     state_.campaign_type = "scenario";
00935 
00936     config::child_list campaigns = game_config_.get_children("campaign");
00937     std::sort(campaigns.begin(),campaigns.end(),less_campaigns_rank);
00938 
00939     std::vector<std::string> campaign_names;
00940     std::vector<std::pair<std::string,std::string> > campaign_desc;
00941 
00942     for(config::child_list::const_iterator i = campaigns.begin(); i != campaigns.end(); ++i) {
00943         std::stringstream str;
00944         const std::string& icon = (**i)["icon"];
00945         const std::string desc = (**i)["description"];
00946         const std::string image = (**i)["image"];
00947         if(icon.empty()) {
00948             str << COLUMN_SEPARATOR;
00949         } else {
00950             str << IMAGE_PREFIX << icon << COLUMN_SEPARATOR;
00951         }
00952 
00953         str << (**i)["name"];
00954 
00955         campaign_names.push_back(str.str());
00956         campaign_desc.push_back(std::pair<std::string,std::string>(desc,image));
00957     }
00958 
00959     if(campaign_names.size() <= 0) {
00960       gui::show_error_message(disp(),
00961                   _("No campaigns are available.\n"));
00962         return false;
00963     }
00964     dialogs::campaign_preview_pane campaign_preview(disp().video(),&campaign_desc);
00965     gui::dialog cmenu(disp(), _("Play a campaign"), " ", gui::OK_CANCEL);
00966     cmenu.set_menu(campaign_names);
00967     cmenu.add_pane(&campaign_preview);
00968     gui::dialog::dimension_measurements dim = cmenu.layout();
00969     Uint16 screen_width = screen_area().w;
00970     Uint16 dialog_width = cmenu.get_frame().get_layout().exterior.w;
00971     if(screen_width < 850 && screen_width - dialog_width > 20) {
00972         // On small resolutions, reduce the amount of unused horizontal space
00973         campaign_preview.set_width(campaign_preview.width() + screen_width - dialog_width - 20);
00974         dim = cmenu.layout();
00975     }
00976     SDL_Rect& preview_loc = dim.panes[&campaign_preview];
00977     preview_loc.y = dim.menu_y;
00978     if(dim.menu_height > 0) {
00979         preview_loc.h = dim.menu_height;
00980     } else {
00981         preview_loc.h = cmenu.get_menu().height();
00982     }
00983     cmenu.set_layout(dim);
00984 
00985     if(cmenu.show() == -1) {
00986         return false;
00987     }
00988 
00989     const config& campaign = *campaigns[cmenu.result()];
00990 
00991     state_.campaign = campaign["id"];
00992     state_.abbrev = campaign["abbrev"];
00993     state_.scenario = campaign["first_scenario"];
00994 
00995     const std::string difficulty_descriptions = campaign["difficulty_descriptions"];
00996     std::vector<std::string> difficulty_options = utils::split(difficulty_descriptions, ';');
00997 
00998     const std::vector<std::string> difficulties = utils::split(campaign["difficulties"]);
00999 
01000     if(difficulties.empty() == false) {
01001         if(difficulty_options.size() != difficulties.size()) {
01002             difficulty_options.resize(difficulties.size());
01003             std::copy(difficulties.begin(),difficulties.end(),difficulty_options.begin());
01004         }
01005 
01006         gui::dialog dlg(disp(), _("Difficulty"),
01007             _("Select difficulty level:"), gui::OK_CANCEL);
01008         dlg.set_menu(difficulty_options);
01009         if(dlg.show() == -1) {
01010             return false;
01011         }
01012 
01013         state_.difficulty = difficulties[dlg.result()];
01014         reset_defines_map();
01015         defines_map_[difficulties[dlg.result()]] = preproc_define();
01016     }
01017 
01018     state_.campaign_define = campaign["define"];
01019     state_.campaign_xtra_defines = utils::split(campaign["extra_defines"]);
01020 
01021     return true;
01022 }
01023 
01024 }
01025 
01026 bool game_controller::goto_campaign()
01027 {
01028     if(jump_to_campaign_){
01029         jump_to_campaign_ = false;
01030         if(new_campaign()) {
01031             play_game(game_controller::RELOAD_DATA);
01032         }else{
01033             return false;
01034         }
01035     }
01036     return true;
01037 }
01038 
01039 bool game_controller::goto_multiplayer()
01040 {
01041     if(jump_to_multiplayer_){
01042         jump_to_multiplayer_ = false;
01043         if(play_multiplayer()){
01044             ;
01045         }else{
01046             return false;
01047         }
01048     }
01049     return true;
01050 }
01051 
01052 static std::string format_file_size(const std::string& size_str)
01053 {
01054     double size = lexical_cast_default<double>(size_str,0.0);
01055 
01056     const double k = 1024;
01057     if(size > 0.0) {
01058         std::string size_postfix = _("B");
01059         if(size > k) {
01060             size /= k;
01061             size_postfix = _("KB");
01062             if(size > k) {
01063                 size /= k;
01064                 size_postfix = _("MB");
01065             }
01066         }
01067 
01068         std::ostringstream stream;
01069 #ifdef _MSC_VER
01070         // Visual C++ makes 'precision' set the number of decimal places.
01071         // Other platforms make it set the number of significant figures
01072         stream.precision(1);
01073         stream << std::fixed << size << size_postfix;
01074 #else
01075         if (size < 100) stream.precision(3);
01076         else size = static_cast<int>(size);
01077         stream << size << size_postfix;
01078 #endif
01079         return stream.str();
01080     } else {
01081         return "";
01082     }
01083 }
01084 
01085 namespace
01086 {
01087     void game_controller::reload_changed_game_config()
01088     {
01089         //force a reload of configuration information
01090         old_defines_map_.clear();
01091         reset_game_cfg();
01092         data_tree_checksum(true); // Reload checksums
01093         refresh_game_cfg();
01094 		::init_textdomains(game_config_);
01095         paths_manager_.set_paths(game_config_);
01096         clear_binary_paths_cache();
01097     }
01098 
01099     // Manage add-ons
01100     void game_controller::manage_addons()
01101     {
01102         int res;
01103         std::string host;
01104         if(new_widgets) {
01105             gui2::taddon_connect addon_dlg;
01106             
01107             addon_dlg.set_host_name(preferences::campaign_server());
01108             addon_dlg.show(disp().video());
01109             
01110             res = addon_dlg.get_retval();
01111             if(res == gui2::tbutton::OK) {
01112                 res = 0;
01113                 host = addon_dlg.host_name();
01114             } 
01115         } else {
01116 
01117             gui::dialog d(disp(),
01118                         _("Connect to Server"),
01119                         _("You will now connect to a server to download add-ons."),
01120                         gui::OK_CANCEL);
01121             d.set_textbox(_("Server: "), preferences::campaign_server());
01122             d.add_button( new gui::dialog_button(disp().video(), _("Remove Add-ons"),
01123                 gui::button::TYPE_PRESS, 2), gui::dialog::BUTTON_EXTRA);
01124             res = d.show();
01125             host = d.textbox_text();
01126         }
01127 
01128         if (res == 0 )  // Get Add-Ons
01129         {
01130             download_campaigns(host);
01131         }
01132         else if (res == 2) // Manage Add-Ons
01133         {
01134 
01135             std::vector<std::string> addons;
01136             std::vector<std::string> addon_dirs;
01137 
01138             const std::string campaign_dir = get_user_data_dir() + "/data/campaigns/";
01139 
01140             get_files_in_dir(campaign_dir, &addons, &addon_dirs, FILE_NAME_ONLY);
01141 
01142             // Strip the ".cfg" extension and replace "_" with " " for display.
01143             std::vector<std::string>::iterator i = addons.begin();
01144             while(i != addons.end())
01145             {
01146                 std::string::size_type pos = i->rfind(".cfg", i->size());
01147                 if(pos == std::string::npos) {
01148                     i = addons.erase(i);
01149                 } else {
01150                     i->erase(pos);
01151                     // remove it from the directory list too
01152                     for(std::vector<std::string>::iterator j = addon_dirs.begin(); j != addon_dirs.end() ; ++j) {
01153                         if (*i == *j) {
01154                             addon_dirs.erase(j);
01155                             break;
01156                         }
01157                     };
01158                     std::replace(i->begin(), i->end(), '_', ' ');
01159                     i++;
01160                 }
01161             }
01162             // process the addons of type Addon/_main.cfg
01163             i = addon_dirs.begin();
01164             while(i != addon_dirs.end())
01165             {
01166                 if (file_exists(campaign_dir + *i + "/_main.cfg")) {
01167                     std::replace(i->begin(), i->end(), '_', ' ');
01168                     addons.push_back(*i);
01169                     i++;
01170                 } else {
01171                     i = addon_dirs.erase(i);
01172                 }
01173             }
01174 
01175             if (addons.empty())
01176             {
01177                 gui::show_error_message(disp(), _("You have no Add-ons installed."));
01178                 return;
01179             }
01180 
01181             gui::menu::basic_sorter sorter;
01182             sorter.set_alpha_sort(1);
01183 
01184 
01185             int index = 0;
01186 
01187             do
01188             {
01189                 gui::dialog addon_dialog(disp(),
01190                              _("Remove Add-ons"), _("Choose the add-on to remove."),
01191                              gui::OK_CANCEL);
01192                 gui::menu::imgsel_style &addon_style = gui::menu::bluebg_style;
01193 
01194                 gui::menu *addon_menu = new gui::menu(disp().video(), addons, false, -1,
01195                         gui::dialog::max_menu_width, &sorter, &addon_style, false);
01196                 addon_dialog.set_menu(addon_menu);
01197                 index = addon_dialog.show();
01198 
01199                 if(index < 0) return;
01200 
01201                 std::string confirm_message = _("Are you sure you want to remove the add-on '$addon|'?");
01202                 utils::string_map symbols;
01203                 symbols["addon"] = addons.at(index);
01204                 confirm_message = utils::interpolate_variables_into_string(confirm_message, &symbols);
01205                 res = gui::dialog(disp(), _("Confirm"), confirm_message, gui::YES_NO).show();
01206             } while (res != 0);
01207 
01208             bool delete_success = true;
01209 
01210             //Put underscores back in the name and remove the addon
01211             std::string filename = addons.at(index);
01212             std::replace(filename.begin(), filename.end(), ' ', '_');
01213             delete_success &= delete_directory(campaign_dir + filename);
01214             //Report results
01215             if (delete_success)
01216             {
01217                 delete_success &= delete_directory(campaign_dir + filename + ".cfg");
01218                 reload_changed_game_config();
01219 
01220                 std::string message = _("Add-on '$addon|' deleted.");
01221                 utils::string_map symbols;
01222                 symbols["addon"] = addons.at(index);
01223                 message = utils::interpolate_variables_into_string(message, &symbols);
01224                 /* GCC-3.3 needs a temp var otherwise compilation fails */
01225                 gui::dialog dlg(disp(), _("Add-on deleted"), message, gui::OK_ONLY);
01226                 dlg.show();
01227             }
01228             else
01229             {
01230                 /* GCC-3.3 needs a temp var otherwise compilation fails */
01231                 gui::dialog dlg2(disp(), _("Error"), _("Add-on could not be deleted -- a file was not found."),
01232                         gui::OK_ONLY);
01233                 dlg2.show();
01234             }
01235     }
01236         else // Cancel or unexpected result
01237             return;
01238     }
01239 
01240 void game_controller::download_campaigns(std::string host)
01241 {
01242     const std::vector<std::string> items = utils::split(host, ':');
01243     if(items.empty()) {
01244         return;
01245     }
01246 
01247     host = items.front();
01248     preferences::set_campaign_server(host);
01249 
01250     try {
01251         const network::manager net_manager;
01252         const network::connection sock = dialogs::network_connect_dialog(disp(), _("Connecting to Server..."),
01253                                         items.front(), lexical_cast_default<int>(items.back(),15003) );
01254         if(!sock) {
01255             gui::show_error_message(disp(), _("Could not connect to host."));
01256             preferences::set_campaign_server("");
01257             return;
01258         }
01259 
01260         config cfg;
01261         cfg.add_child("request_campaign_list");
01262         // @todo Should be enabled once the campaign server can be recompiled.
01263         network::send_data(cfg, sock, false);
01264 
01265         network::connection res = dialogs::network_receive_dialog(disp(),_("Asking for list of add-ons"),cfg,sock);
01266         if(!res) {
01267             return;
01268         }
01269 
01270         const config* const error = cfg.child("error");
01271         if(error != NULL) {
01272             gui::show_error_message(disp(), (*error)["message"]);
01273             return;
01274         }
01275 
01276         const config* const campaigns_cfg = cfg.child("campaigns");
01277         if(campaigns_cfg == NULL) {
01278             gui::show_error_message(disp(), _("Error communicating with the server."));
01279             return;
01280         }
01281 
01282         std::vector<std::string> campaigns, versions, uploads, options;
01283 
01284         std::string sep(1, COLUMN_SEPARATOR);
01285 
01286         std::stringstream heading;
01287         heading << HEADING_PREFIX << sep << _("Name") << sep << _("Version") << sep
01288                 << _("Author") << sep << _("Downloads") << sep << _("Size");
01289 
01290         const config::child_list& cmps = campaigns_cfg->get_children("campaign");
01291         const std::vector<std::string>& publish_options = available_campaigns();
01292 
01293         std::vector<std::string> delete_options;
01294 
01295         std::vector<int> sizes;
01296 
01297         for(config::child_list::const_iterator i = cmps.begin(); i != cmps.end(); ++i) {
01298             const std::string& name = (**i)["name"];
01299             campaigns.push_back(name);
01300             versions.push_back((**i)["version"]);
01301             uploads.push_back((**i)["uploads"]);
01302 
01303             if(std::count(publish_options.begin(),publish_options.end(),name) != 0) {
01304                 delete_options.push_back(name);
01305             }
01306 
01307             std::string title = (**i)["title"];
01308             if(title == "") {
01309                 title = name;
01310                 std::replace(title.begin(),title.end(),'_',' ');
01311             }
01312 
01313             std::string version   = (**i)["version"],
01314                         author    = (**i)["author"];
01315 
01316             utils::truncate_as_wstring(title, 20);
01317             utils::truncate_as_wstring(version, 12);
01318             utils::truncate_as_wstring(author, 16);
01319 
01320             //add negative sizes to reverse the sort order
01321             sizes.push_back(-atoi((**i)["size"].c_str()));
01322 
01323             std::string icon = (**i)["icon"];
01324             if(icon.find("units/") != std::string::npos
01325             && icon.find_first_of('~') == std::string::npos) {
01326                 //a hack to prevent magenta icons, because they look awful
01327                 icon.append("~RC(magenta>red)");
01328             }
01329             options.push_back(IMAGE_PREFIX + icon + COLUMN_SEPARATOR +
01330                               title + COLUMN_SEPARATOR +
01331                               version + COLUMN_SEPARATOR +
01332                               author + COLUMN_SEPARATOR +
01333                               (**i)["downloads"].str() + COLUMN_SEPARATOR +
01334                               format_file_size((**i)["size"]));
01335         }
01336 
01337         options.push_back(heading.str());
01338 
01339         for(std::vector<std::string>::const_iterator j = publish_options.begin(); j != publish_options.end(); ++j) {
01340             options.push_back(sep + _("Publish add-on: ") + *j);
01341         }
01342 
01343         for(std::vector<std::string>::const_iterator d = delete_options.begin(); d != delete_options.end(); ++d) {
01344             options.push_back(sep + _("Delete add-on: ") + *d);
01345         }
01346 
01347         if(campaigns.empty() && publish_options.empty()) {
01348             gui::show_error_message(disp(), _("There are no add-ons available for download from this server."));
01349             return;
01350         }
01351 
01352         gui::menu::basic_sorter sorter;
01353         sorter.set_alpha_sort(1).set_alpha_sort(2).set_alpha_sort(3).set_numeric_sort(4).set_position_sort(5,sizes);
01354 
01355         gui::dialog addon_dialog(disp(), _("Get Add-ons"),
01356                            _("Choose the add-on to download."),
01357                            gui::OK_CANCEL);
01358         gui::menu::imgsel_style addon_style(gui::menu::bluebg_style);
01359 
01360         //make sure the icon isn't too big
01361         addon_style.scale_images(font::relative_size(72), font::relative_size(72));
01362         gui::menu *addon_menu = new gui::menu(disp().video(), options, false, -1,
01363             gui::dialog::max_menu_width, &sorter, &addon_style, false);
01364         addon_dialog.set_menu(addon_menu);
01365         const int index = addon_dialog.show();
01366         if(index < 0) {
01367             return;
01368         }
01369 
01370         if(index >= int(campaigns.size() + publish_options.size())) {
01371             delete_campaign(delete_options[index - int(campaigns.size() + publish_options.size())],sock);
01372             return;
01373         }
01374 
01375         if(index >= int(campaigns.size())) {
01376             upload_campaign(publish_options[index - int(campaigns.size())],sock);
01377             return;
01378         }
01379 
01380         // Get all dependencies of the campaign selected for download.
01381         const config *selected_campaign = campaigns_cfg->find_child("campaign", "name", campaigns[index]);
01382         std::vector<std::string> dependencies = utils::split((*selected_campaign)["dependencies"]);
01383         if (!dependencies.empty()) {
01384             // Get all dependencies which are not already installed.
01385             // TODO: Somehow determine if the version is outdated.
01386             const std::vector<std::string>& installed = installed_campaigns();
01387             std::vector<std::string>::iterator i;
01388             std::string missing = "";
01389             for (i = dependencies.begin(); i != dependencies.end(); i++) {
01390                 if (std::find(installed.begin(), installed.end(), *i) == installed.end()) {
01391                     missing += "\n" + *i;
01392                 }
01393             }
01394             // If there are any, display a message.
01395             // TODO: Somehow offer to automatically download
01396             // the missing dependencies.
01397             if (!missing.empty()) {
01398                 if (gui::dialog(disp(),
01399                               _("Dependencies"),
01400                               std::string(_("This add-on requires the following additional dependencies:")) +
01401                             "\n" + missing +
01402                             "\n" + _("Do you still want to download it?"), gui::OK_CANCEL).show())
01403                     return;
01404             }
01405         }
01406 
01407         config request;
01408         request.add_child("request_campaign")["name"] = campaigns[index];
01409         // @todo Should be enabled once the campaign server can be recompiled.
01410         network::send_data(request, sock, false);
01411 
01412         res = dialogs::network_receive_dialog(disp(),_("Downloading add-on..."),cfg,sock);
01413         if(!res) {
01414             return;
01415         }
01416 
01417         if(cfg.child("error") != NULL) {
01418             gui::show_error_message(disp(), (*cfg.child("error"))["message"]);
01419             return;
01420         }
01421 
01422         if(!check_names_legal(cfg)) {
01423             gui::show_error_message(disp(), "The add-on has an invalid file or directory name and can not be installed.");
01424             return;
01425         }
01426 
01427         //remove any existing versions of the just downloaded campaign
01428         //assuming it consists of a dir and a cfg file
01429         remove_campaign(campaigns[index]);
01430 
01431         //add revision info to the addon
01432                 config *maindir = cfg.find_child("dir", "name", campaigns[index]);
01433                 if (maindir) {
01434                     config f;
01435                     f["name"] = "info.cfg";
01436                     std::string s;
01437                     s += "[info]\n";
01438                     s += "version=\"" + versions[index] + "\"\n";
01439                     s += "uploads=\"" + uploads[index] + "\"\n";
01440                     s += "[/info]\n";
01441                     f["contents"] = s;
01442                     maindir->add_child("file", f);
01443                 }
01444 
01445         //put a break at line below to see that it really works.
01446         unarchive_campaign(cfg);
01447 
01448         reload_changed_game_config();
01449 
01450         std::string warning = "";
01451         std::vector<config *> scripts = find_scripts(cfg, ".unchecked");
01452         if (!scripts.empty()) {
01453             warning += "\nUnchecked script files found:";
01454             std::vector<config *>::iterator i;
01455             for (i = scripts.begin(); i != scripts.end(); ++i) {
01456                 warning += "\n" + (**i)["name"];
01457             }
01458         }
01459 
01460         /* GCC-3.3 needs a temp var otherwise compilation fails */
01461         gui::message_dialog dlg(disp(),_("Add-on Installed"),_("The add-on has been installed."));
01462         dlg.show();
01463     } catch(config::error&) {
01464         gui::show_error_message(disp(), _("Network communication error."));
01465     } catch(network::error&) {
01466         gui::show_error_message(disp(), _("Remote host disconnected."));
01467     } catch(io_exception&) {
01468         gui::show_error_message(disp(), _("There was a problem creating the files necessary to install this add-on."));
01469     } catch(twml_exception& e) {
01470         e.show(disp());
01471     }
01472 }
01473 
01474 void game_controller::upload_campaign(const std::string& campaign, network::connection sock)
01475 {
01476     config request_terms;
01477     request_terms.add_child("request_terms");
01478     // @todo Should be enabled once the campaign server can be recompiled.
01479     network::send_data(request_terms, sock, false);
01480     config data;
01481     sock = network::receive_data(data,sock,5000);
01482     if(!sock) {
01483         gui::show_error_message(disp(), _("Connection timed out"));
01484         return;
01485     } else if(data.child("error")) {
01486         std::string error_message = _("The server responded with an error: \"$error|\"");
01487         utils::string_map symbols;
01488         symbols["error"] = (*data.child("error"))["message"].str();
01489         error_message = utils::interpolate_variables_into_string(error_message, &symbols);
01490         gui::show_error_message(disp(), error_message);
01491         return;
01492     } else if(data.child("message")) {
01493         const int res = gui::dialog(disp(),_("Terms"),(*data.child("message"))["message"],gui::OK_CANCEL).show();
01494         if(res != 0) {
01495             return;
01496         }
01497     }
01498 
01499     config cfg;
01500     get_campaign_info(campaign,cfg);
01501 
01502     std::string passphrase = cfg["passphrase"];
01503     if(passphrase.empty()) {
01504         passphrase.resize(8);
01505         for(size_t n = 0; n != 8; ++n) {
01506             passphrase[n] = 'a' + (rand()%26);
01507         }
01508         cfg["passphrase"] = passphrase;
01509         set_campaign_info(campaign,cfg);
01510     }
01511 
01512     cfg["name"] = campaign;
01513 
01514     config campaign_data;
01515     archive_campaign(campaign,campaign_data);
01516 
01517     data.clear();
01518     data.add_child("upload",cfg).add_child("data",campaign_data);
01519 
01520     LOG_NET << "uploading campaign...\n";
01521     // @todo Should be enabled once the campaign server can be recompiled.
01522     network::send_data(data, sock, false);
01523 
01524     sock = dialogs::network_send_dialog(disp(),_("Sending add-on"),data,sock);
01525     if(!sock) {
01526         return;
01527     } else if(data.child("error")) {
01528         gui::show_error_message(disp(), _("The server responded with an error: \"") +
01529                                 (*data.child("error"))["message"].str() + '"');
01530     } else if(data.child("message")) {
01531         /* GCC-3.3 needs a temp var otherwise compilation fails */
01532         gui::message_dialog dlg(disp(),_("Response"),(*data.child("message"))["message"]);
01533         dlg.show();
01534     }
01535 }
01536 
01537 void game_controller::delete_campaign(const std::string& campaign, network::connection sock)
01538 {
01539     config cfg;
01540     get_campaign_info(campaign,cfg);
01541 
01542     config msg;
01543     msg["name"] = campaign;
01544     msg["passphrase"] = cfg["passphrase"];
01545 
01546     config data;
01547     data.add_child("delete",msg);
01548 
01549     // @todo Should be enabled once the campaign server can be recompiled.
01550     network::send_data(data, sock, false);
01551 
01552     sock = network::receive_data(data,sock,5000);
01553     if(!sock) {
01554         gui::show_error_message(disp(), _("Connection timed out"));
01555     } else if(data.child("error")) {
01556         gui::show_error_message(disp(), _("The server responded with an error: \"") +
01557                                 (*data.child("error"))["message"].str() + '"');
01558     } else if(data.child("message")) {
01559         /* GCC-3.3 needs a temp var otherwise compilation fails */
01560         gui::message_dialog dlg(disp(),_("Response"),(*data.child("message"))["message"]);
01561         dlg.show();
01562     }
01563 }
01564 
01565 void game_controller::remove_campaign(const std::string& campaign)
01566 {
01567     const std::string campaign_dir = get_user_data_dir() + "/data/campaigns/" + campaign;
01568     delete_directory(campaign_dir);
01569     if (file_exists(campaign_dir + ".cfg"))
01570         delete_directory(campaign_dir + ".cfg");
01571 }
01572 
01573 
01574 bool game_controller::play_multiplayer()
01575 {
01576     int res;
01577 
01578     state_ = game_state();
01579     state_.campaign_type = "multiplayer";
01580     state_.campaign_define = "MULTIPLAYER";
01581 
01582 
01583 
01584     //Print Gui only if the user hasn't specified any server
01585     if( multiplayer_server_.empty() ){
01586         if(new_widgets) {
01587             gui2::tmp_method_selection dlg;
01588 
01589                 dlg.show(disp().video());
01590                 
01591                 if(dlg.get_retval() == gui2::tbutton::OK) {
01592                     std::cerr << "OK\n";
01593                     res = dlg.get_choice();
01594                 } else {
01595                     std::cerr << "CANCEL\n";
01596                     return false;
01597 
01598                 }
01599 
01600         } else {
01601 
01602             std::vector<std::string> host_or_join;
01603             std::string const pre = IMAGE_PREFIX + std::string("icons/icon-");
01604             char const sep1 = COLUMN_SEPARATOR, sep2 = HELP_STRING_SEPARATOR;
01605 
01606             host_or_join.push_back(pre + "server.png"
01607                 + sep1 + _("Join Official Server")
01608                 + sep2 + _("Log on to the official Wesnoth multiplayer server"));
01609             host_or_join.push_back(pre + "serverother.png"
01610                 + sep1 + _("Connect to Server")
01611                 + sep2 + _("Join a different server"));
01612             host_or_join.push_back(pre + "hotseat.png"
01613                 + sep1 + _("Local Game")
01614                 + sep2 + _("Play a multiplayer game with the AI or humans sharing the same machine"));
01615 
01616             std::string login = preferences::login();
01617 
01618             {
01619                 gui::dialog d(disp(), _("Multiplayer"), "", gui::OK_CANCEL);
01620                 d.set_menu(host_or_join);
01621                 d.set_textbox(_("Login: "), login, mp::max_login_size, font::relative_size(250));
01622                 res = d.show();
01623                 login = d.textbox_text();
01624             }
01625             if (res < 0)
01626                 return false;
01627 
01628 
01629             preferences::set_login(login);
01630         }
01631 
01632     }else{
01633         res = 3;
01634     }
01635 
01636     try {
01637 
01638         /* do */ {
01639             reset_defines_map();
01640             defines_map_[state_.campaign_define] = preproc_define();
01641             refresh_game_cfg();
01642             events::discard(INPUT_MASK); // prevent the "keylogger" effect
01643             cursor::set(cursor::NORMAL);
01644         }
01645 
01646         if(res == 2) {
01647             std::vector<std::string> chat;
01648             config game_data;
01649 
01650             const mp::controller cntr = mp::CNTR_LOCAL;
01651             const bool is_server = false;
01652 
01653             mp::start_server(disp(), game_config_, cntr, is_server);
01654 
01655         } else if(res == 0 || res == 1 || res == 3 ) {
01656             std::string host;
01657             if(res == 0) {
01658                 host = preferences::server_list().front().address;
01659             }else if(res == 3){
01660                 host = multiplayer_server_;
01661                 multiplayer_server_ = "";
01662             }
01663             mp::start_client(disp(), game_config_, host);
01664         }
01665     } catch(game::load_game_failed& e) {
01666         gui::show_error_message(disp(), _("The game could not be loaded: ") + e.message);
01667     } catch(game::game_error& e) {
01668         gui::show_error_message(disp(), _("Error while playing the game: ") + e.message);
01669     } catch(network::error& e) {
01670         if(e.message != "") {
01671             ERR_NET << "caught network::error: " << e.message << "\n";
01672             /* GCC-3.3 needs a temp var otherwise compilation fails */
01673             gui::dialog dlg(disp(),"",e.message,gui::OK_ONLY);
01674             dlg.show();
01675         } else {
01676             ERR_NET << "caught network::error\n";
01677         }
01678     } catch(config::error& e) {
01679         if(e.message != "") {
01680             ERR_CONFIG << "caught config::error: " << e.message << "\n";
01681             /* GCC-3.3 needs a temp var otherwise compilation fails */
01682             gui::dialog dlg2(disp(),"",e.message,gui::OK_ONLY);
01683             dlg2.show();
01684         } else {
01685             ERR_CONFIG << "caught config::error\n";
01686         }
01687     } catch(gamemap::incorrect_format_exception& e) {
01688         gui::show_error_message(disp(), std::string(_("The game map could not be loaded: ")) + e.msg_);
01689     } catch(game::load_game_exception& e) {
01690         //this will make it so next time through the title screen loop, this game is loaded
01691         loaded_game_ = e.game;
01692         loaded_game_show_replay_ = e.show_replay;
01693         loaded_game_cancel_orders_ = e.cancel_orders;
01694     } catch(twml_exception& e) {
01695         e.show(disp());
01696     }
01697 
01698     return false;
01699 }
01700 
01701 bool game_controller::change_language()
01702 {
01703     if(new_widgets) {
01704             gui2::tlanguage_selection dlg;
01705 
01706             dlg.show(disp().video());
01707             
01708             if(dlg.get_retval() == gui2::tbutton::OK) {
01709                 if(!no_gui_) {
01710                     std::string wm_title_string = _("The Battle for Wesnoth");
01711                     wm_title_string += " - " + game_config::revision;
01712                     SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
01713                 }
01714 
01715                 refresh_game_cfg(true);
01716             }
01717 
01718     } else {
01719         const std::vector<language_def>& languages = get_languages();
01720         std::vector<std::string> langs;
01721 
01722         for (std::vector<language_def>::const_iterator itor = languages.begin();
01723                 itor != languages.end(); ++itor) {
01724             if (*itor == get_language()) {
01725                 langs.push_back("*" + itor->language);
01726             } else {
01727                 langs.push_back(itor->language);
01728             }
01729         }
01730 
01731         gui::dialog lmenu(disp(),_("Language"),
01732                                  _("Choose your preferred language:"),
01733                                  gui::OK_CANCEL);
01734         lmenu.set_menu(langs);
01735         const int res = lmenu.show();
01736         if(size_t(res) < langs.size()) {
01737 			::set_language(languages[res]);
01738             preferences::set_language(languages[res].localename);
01739 
01740             if(!no_gui_) {
01741                 std::string wm_title_string = _("The Battle for Wesnoth");
01742                 wm_title_string += " - " + game_config::revision;
01743                 SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
01744             }
01745 
01746             refresh_game_cfg(true);
01747         } else {
01748             return false;
01749         }
01750 
01751         font::load_font_config();
01752         hotkey::load_descriptions();
01753     }
01754     return true;
01755 }
01756 
01757 void game_controller::show_preferences()
01758 {
01759     const preferences::display_manager disp_manager(&disp());
01760     preferences::show_preferences_dialog(disp(),game_config_);
01761 
01762     disp().redraw_everything();
01763 }
01764 
01765 void game_controller::show_upload_begging()
01766 {
01767     upload_log_dialog::show_beg_dialog(disp());
01768 
01769     disp().redraw_everything();
01770 }
01771 
01772 
01773 void game_controller::read_configs(std::string& error_log)
01774 {
01775     preproc_map defines_map(defines_map_);
01776 
01777     std::string user_error_log;
01778     //read the file and then write to the cache
01779     scoped_istream stream = preprocess_file("data/", &defines_map, &error_log);
01780 
01781     //reset the parse counter before reading the game files
01782     if (loadscreen::global_loadscreen) {
01783         loadscreen::global_loadscreen->parser_counter = 0;
01784     }
01785 
01786     read(game_config_, *stream, &error_log);
01787 
01788     //load usermade add-ons
01789     const std::string user_campaign_dir = get_user_data_dir() + "/data/campaigns/";
01790     std::vector<std::string> user_campaigns, error_campaigns;
01791     get_files_in_dir(user_campaign_dir,NULL,&user_campaigns,ENTIRE_FILE_PATH);
01792     for(std::vector<std::string>::const_iterator uc = user_campaigns.begin(); uc != user_campaigns.end(); ++uc) {
01793         std::string oldstyle_cfg = *uc + ".cfg";
01794         std::string main_cfg = *uc + "/_main.cfg";
01795         std::string toplevel;
01796         if (file_exists(oldstyle_cfg))
01797             toplevel = oldstyle_cfg;
01798         else if (file_exists(main_cfg))
01799             toplevel = main_cfg;
01800         else
01801             continue;
01802 
01803         try {
01804             preproc_map user_defines_map(defines_map);
01805             scoped_istream stream = preprocess_file(toplevel, &user_defines_map);
01806 
01807             std::string campaign_error_log;
01808 
01809             config user_campaign_cfg;
01810             read(user_campaign_cfg,*stream,&campaign_error_log);
01811 
01812             if(campaign_error_log.empty()) {
01813                 game_config_.append(user_campaign_cfg);
01814             } else {
01815                 user_error_log += campaign_error_log;
01816                 error_campaigns.push_back(*uc);
01817             }
01818         } catch(config::error& err) {
01819             ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
01820             error_campaigns.push_back(*uc);
01821 
01822             user_error_log += err.message + "\n";
01823         } catch(preproc_config::error&) {
01824             ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
01825             error_campaigns.push_back(*uc);
01826             //no need to modify the error log here, already done by the preprocessor
01827 
01828         } catch(io_exception&) {
01829             ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
01830             error_campaigns.push_back(*uc);
01831         }
01832     }
01833 
01834     if(error_campaigns.empty() == false) {
01835         std::stringstream msg;
01836         msg << _n("The following add-on had errors and could not be loaded:",
01837                 "The following add-ons had errors and could not be loaded:",
01838                 error_campaigns.size());
01839         for(std::vector<std::string>::const_iterator i = error_campaigns.begin(); i != error_campaigns.end(); ++i) {
01840             msg << "\n" << *i;
01841         }
01842 
01843         msg << "\n" << _("ERROR DETAILS:") << "\n" << font::nullify_markup(user_error_log);
01844 
01845         gui::show_error_message(disp(),msg.str());
01846     }
01847 
01848     game_config_.merge_children("units");
01849 
01850     config& hashes = game_config_.add_child("multiplayer_hashes");
01851     for(config::child_list::const_iterator ch = game_config_.get_children("multiplayer").begin(); ch != game_config_.get_children("multiplayer").end(); ++ch) {
01852         hashes[(**ch)["id"]] = (*ch)->hash();
01853     }
01854 
01855 }
01856 
01857 //this function reads the game configuration, searching for valid cached copies first
01858 void game_controller::read_game_cfg(bool use_cache)
01859 {
01860     log_scope("read_game_cfg");
01861 
01862     loadscreen::global_loadscreen_manager loadscreen_manager(disp().video());
01863 
01864     bool is_valid = true;
01865     std::stringstream str;
01866     for(preproc_map::const_iterator i = defines_map_.begin(); i != defines_map_.end(); ++i) {
01867         if(i->second.value != "" || i->second.arguments.empty() == false) {
01868             is_valid = false;
01869             break;
01870         }
01871 
01872         str << " " << i->first;
01873     }
01874     //std::string localename = get_locale().localename;
01875     //str << "-lang_" << (localename.empty() ? "default" : localename);
01876 
01877     if(is_valid) {
01878         const std::string& cache = get_cache_dir();
01879         if(cache != "") {
01880             sha1_hash sha(str.str()); // use a hash for a shorter display of the defines
01881             const std::string fname = cache + "/cache-v" + game_config::version + "-" + sha.display();
01882             const std::string fname_checksum = fname + ".checksum";
01883 
01884             file_tree_checksum dir_checksum;
01885 
01886             if(use_cache && !force_valid_cache_) {
01887                 try {
01888                     if(file_exists(fname_checksum)) {
01889                         config checksum_cfg;
01890                         scoped_istream stream = istream_file(fname_checksum);
01891                         read(checksum_cfg, *stream);
01892                         dir_checksum = file_tree_checksum(checksum_cfg);
01893                     }
01894                 } catch(config::error&) {
01895                     ERR_CONFIG << "cache checksum is corrupt\n";
01896                 } catch(io_exception&) {
01897                     ERR_CONFIG << "error reading cache checksum\n";
01898                 }
01899             }
01900 
01901             if(force_valid_cache_) {
01902                 LOG_CONFIG << "skipping cache validation (forced)\n";
01903             }
01904 
01905             if(use_cache && file_exists(fname) && (force_valid_cache_ || (dir_checksum == data_tree_checksum()))) {
01906                 LOG_CONFIG << "found valid cache at '" << fname << "' using it\n";
01907                 log_scope("read cache");
01908                 try {
01909                     scoped_istream stream = istream_file(fname);
01910                     read_compressed(game_config_, *stream);
01911                     set_unit_data();
01912                     return;
01913                 } catch(config::error&) {
01914                     ERR_CONFIG << "cache is corrupt. Loading from files\n";
01915                 } catch(io_exception&) {
01916                     ERR_CONFIG << "error reading cache. Loading from files\n";
01917                 }
01918             }
01919 
01920             LOG_CONFIG << "no valid cache found. Writing cache to '" << fname << " with defines_map "<< str.str() << "'\n";
01921             DBG_CONFIG << ((use_cache && file_exists(fname)) ? "yes":"no ") << " " << dir_checksum.modified << "==" << data_tree_checksum().modified << "  " << dir_checksum.nfiles << "==" << data_tree_checksum().nfiles << "  " << dir_checksum.sum_size << "==" << data_tree_checksum().sum_size << "\n";
01922 
01923             std::string error_log;
01924 
01925             read_configs(error_log);
01926 
01927             if(!error_log.empty()) {
01928                 gui::show_error_message(disp(),
01929                         _("Warning: Errors occurred while loading game configuration files: '") +
01930                         font::nullify_markup(error_log));
01931 
01932             } else {
01933                 try {
01934                     scoped_ostream cache = ostream_file(fname);
01935                     write_compressed(*cache, game_config_);
01936                     config checksum_cfg;
01937                     data_tree_checksum().write(checksum_cfg);
01938                     scoped_ostream checksum = ostream_file(fname_checksum);
01939                     write(*checksum, checksum_cfg);
01940                 } catch(io_exception&) {
01941                     ERR_FS << "could not write to cache '" << fname << "'\n";
01942                 }
01943             }
01944 
01945             set_unit_data();
01946             return;
01947         }
01948     }
01949 
01950     ERR_CONFIG << "caching cannot be done. Reading file\n";
01951 
01952     std::string error_log;
01953 
01954     read_configs(error_log);
01955     if(!error_log.empty()) {
01956         gui::show_error_message(disp(),
01957                 _("Warning: Errors occurred while loading game configuration files: '") +
01958                 font::nullify_markup(error_log));
01959 
01960     }
01961     set_unit_data();
01962 }
01963 
01964 void game_controller::set_unit_data(){
01965     const config* const units = game_config_.child("units");
01966     if(units != NULL) {
01967         unit_type_data::types().set_config(*units);
01968     }
01969 }
01970 
01971 void game_controller::refresh_game_cfg(bool reset_translations)
01972 {
01973     try {
01974         if(old_defines_map_.empty() || defines_map_ != old_defines_map_ || reset_translations) {
01975             cursor::setter cur(cursor::WAIT);
01976 
01977             if(!reset_translations) {
01978                 game_config_.clear();
01979                 read_game_cfg(use_caching_);
01980             } else {
01981                 game_config_.reset_translation();
01982                 // we may have translatable strings in [game_config]
01983                 // e.g. team color names are defined there
01984                 game_config::load_config(game_config_.child("game_config"));
01985             }
01986 
01987             old_defines_map_ = defines_map_;
01988         }
01989     } catch(config::error& e) {
01990         ERR_CONFIG << "Error loading game configuration files\n";
01991         gui::show_error_message(disp(), _("Error loading game configuration files: '") +
01992             font::nullify_markup(e.message) + _("' (The game will now exit)"));
01993         throw e;
01994     } catch(preproc_config::error& e) {
01995         ERR_CONFIG << "Error loading game configuration files\n";
01996         gui::show_error_message(disp(), _("Error loading game configuration files: '") +
01997             font::nullify_markup(e.message) + _("' (The game will now exit)"));
01998         throw e;
01999     }
02000 }
02001 
02002 void game_controller::reset_game_cfg()
02003 {
02004     reset_defines_map();
02005 
02006     //load in the game's configuration files
02007 #if defined(__APPLE__)
02008     defines_map_["APPLE"] = preproc_define();
02009 #endif
02010 
02011     if(multiplayer_mode_) {
02012         defines_map_["MULTIPLAYER"] = preproc_define();
02013     } else {
02014         defines_map_["NORMAL"] = preproc_define();
02015         defines_map_["MEDIUM"] = preproc_define();
02016     }
02017 
02018     //refresh_game_cfg();
02019 }
02020 
02021 void game_controller::reset_defines_map()
02022 {
02023     defines_map_.clear();
02024 
02025 /* APPLE is only meant for configuration so it's not there */
02026 
02027 #ifdef USE_TINY_GUI
02028     defines_map_["TINY"] = preproc_define();
02029 #endif
02030 
02031 #ifdef USE_SMALL_GUI
02032     defines_map_["SMALL_GUI"] = preproc_define();
02033 #endif
02034 
02035 #ifdef HAVE_PYTHON
02036     defines_map_["PYTHON"] = preproc_define();
02037 #endif
02038 }
02039 
02040 void game_controller::play_game(RELOAD_GAME_DATA reload)
02041 {
02042     loadscreen::global_loadscreen_manager loadscreen_manager(disp().video());
02043     loadscreen::global_loadscreen->set_progress(0, _("Loading data files"));
02044     if(reload == RELOAD_DATA) {
02045         if(state_.campaign_define.empty() == false) {
02046             defines_map_[state_.campaign_define] = preproc_define();
02047         }
02048 
02049         for( std::vector<std::string>::const_iterator i = state_.campaign_xtra_defines.begin();
02050              i != state_.campaign_xtra_defines.end(); ++i) {
02051             defines_map_[*i] = preproc_define();
02052         }
02053 
02054         if(defines_map_.count("NORMAL")) {
02055             defines_map_["MEDIUM"] = preproc_define();
02056         }
02057 
02058         try {
02059             refresh_game_cfg();
02060         } catch(config::error&) {
02061             reset_game_cfg();
02062             refresh_game_cfg();
02063             return;
02064         }
02065     }
02066 
02067     loadscreen::global_loadscreen->set_progress(60);
02068 
02069     const binary_paths_manager bin_paths_manager(game_config_);
02070 
02071     try {
02072         // Only record log for single-player games & tutorial.
02073         upload_log log(state_.campaign_type.empty()
02074                        || state_.campaign_type == "scenario"
02075                        || state_.campaign_type == "tutorial");
02076 
02077         const LEVEL_RESULT result = ::play_game(disp(),state_,game_config_, log);
02078         // don't show The End for multiplayer scenario
02079         // change this if MP campaigns are implemented
02080         if((result == VICTORY || result == LEVEL_CONTINUE_NO_SAVE) && (state_.campaign_type.empty() || state_.campaign_type != "multiplayer")) {
02081             the_end(disp());
02082             about::show_about(disp(),state_.campaign);
02083         }
02084     } catch(game::load_game_exception& e) {
02085 
02086         //this will make it so next time through the title screen loop, this game is loaded
02087         loaded_game_ = e.game;
02088         loaded_game_show_replay_ = e.show_replay;
02089         loaded_game_cancel_orders_ = e.cancel_orders;
02090 
02091     } catch(twml_exception& e) {
02092         e.show(disp());
02093     }
02094 }
02095 
02096 } //end anon namespace
02097 
02098 void game_controller::play_replay()
02099 {
02100     const binary_paths_manager bin_paths_manager(game_config_);
02101 
02102     try {
02103 		::play_replay(disp(),state_,game_config_,video_);
02104 
02105     } catch(game::load_game_exception& e) {
02106 
02107         //this will make it so next time through the title screen loop, this game is loaded
02108         loaded_game_ = e.game;
02109         loaded_game_show_replay_ = e.show_replay;
02110         loaded_game_cancel_orders_ = e.cancel_orders;
02111 
02112     } catch(twml_exception& e) {
02113         e.show(disp());
02114     }
02115 }
02116 
02117 game_controller::~game_controller()
02118 {
02119     delete gui::empty_menu;
02120     sound::close_sound();
02121 }
02122 
02123 // this is needed to allow identical functionality with clean refactoring
02124 // play_game only returns on an error, all returns within play_game can
02125 // be replaced with this
02126 static void safe_exit(int res) {
02127 
02128     LOG_GENERAL << "exiting with code " << res << "\n";
02129 #ifdef OS2 /* required to correctly shutdown SDL on OS/2 */
02130         SDL_Quit();
02131 #endif
02132     exit(res);
02133 }
02134 
02135 // maybe this should go in a util file somewhere?
02136 static void gzip_codec(const std::string & input_file, const std::string & output_file, bool encode)
02137 {
02138     try {
02139     std::ofstream ofile(output_file.c_str(), std::ios_base::out
02140             | std::ios_base::binary | std::ios_base::binary);
02141             std::ifstream ifile(input_file.c_str(),
02142             std::ios_base::in | std::ios_base::binary);
02143         boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
02144         if(encode)
02145             in.push(boost::iostreams::gzip_compressor());
02146         else
02147             in.push(boost::iostreams::gzip_decompressor());
02148         in.push(ifile);
02149         boost::iostreams::copy(in, ofile);
02150             ifile.close();
02151         safe_exit(remove(input_file.c_str()));
02152         }  catch(io_exception& e) {
02153         std::cerr << "IO error: " << e.what() << "\n";
02154     }
02155 }
02156 
02157 static void gzip_encode(const std::string & input_file, const std::string & output_file)
02158 {
02159     gzip_codec(input_file, output_file, true);
02160 }
02161 
02162 static void gzip_decode(const std::string & input_file, const std::string & output_file)
02163 {
02164     gzip_codec(input_file, output_file, false);
02165 }
02166 
02167 
02168 //! Process commandline-arguments
02169 static int play_game(int argc, char** argv)
02170 {
02171     //parse arguments that shouldn't require a display device
02172     int arg;
02173     for(arg = 1; arg != argc; ++arg) {
02174         const std::string val(argv[arg]);
02175         if(val.empty()) {
02176             continue;
02177         }
02178 
02179         if(val == "--help" || val == "-h") {
02180             // When adding items don't forget to update doc/man/wesnoth.6
02181             std::cout << "usage: " << argv[0]
02182             << " [OPTIONS] [DATA-DIRECTORY]\n"
02183             << "  --bpp number                 sets BitsPerPixel value. Example: --bpp 32\n"
02184             << "  --compress INFILE OUTFILE    compresses a savefile (INFILE) that is in text WML\n"
02185             << "                               format into binary WML format (OUTFILE).\n"
02186             << "  -d, --debug                  shows extra debugging information and enables\n"
02187             << "                               additional command mode options in-game.\n"
02188             << "  --decompress INFILE OUTFILE  decompresses a savefile (INFILE) that is in binary\n"
02189             << "                               WML format into text WML format (OUTFILE).\n"
02190             << "  -f, --fullscreen             runs the game in full screen mode.\n"
02191             << "  --fps                        displays the number of frames per second the game\n"
02192             << "                               is currently running at, in a corner of the screen.\n"
02193             << "  --gunzip INFILE.gz           decompresses a file (INFILE.gz) in gzip format\n"
02194             << "                               and stores it without the .gz suffix.\n"
02195             << "                               INFILE.gz will be removed.\n"
02196             << "  --gzip INFILE                compresses a file (INFILE) in gzip format,\n"
02197             << "                               stores it as INFILE.gz and removes INFILE.\n"
02198             << "  -h, --help                   prints this message and exits.\n"
02199             << "  --load SAVEGAME              loads the file SAVEGAME from the standard save\n"
02200             << "                               game directory.\n"
02201             << "  --with-replay                replays the file SAVEGAME loaded with --load option.\n"
02202             << "  --log-<level>=<domain1>,<domain2>,...\n"
02203             << "                               sets the severity level of the log domains.\n"
02204             << "                               'all' can be used to match any log domain.\n"
02205             << "                               Available levels: error, warning, info, debug.\n"
02206             << "                               By default the 'error' level is used.\n"
02207             << "  --logdomains                 List defined log domains and exit.\n"
02208             << "  --nocache                    disables caching of game data.\n"
02209             << "  --validcache                 assume that cache is valid (dangerous)\n"
02210             << "  --nosound                    runs the game without sounds and music.\n"
02211             << "  --max-fps                    the maximum fps the game tries to run at the value\n"
02212             << "                               should be between the 1 and 1000, the default is 50.\n"
02213             << "  --path                       prints the name of the game data directory and exits.\n"
02214 #ifdef HAVE_PYTHON
02215             << "  --python-api                 prints the runtime documentation for the python API.\n"
02216 #endif
02217             << "  -r, --resolution XxY         sets the screen resolution. Example: -r 800x600\n"
02218             << "  -t, --test                   runs the game in a small test scenario.\n"
02219             << "  -v, --version                prints the game's version number and exits.\n"
02220             << "  -w, --windowed               runs the game in windowed mode.\n"
02221             << "  --no-delay                   run the game without any delays.\n"
02222             << "  -c, --campaign               skip menu, and go directly to campaign selection menu.\n"
02223             << "  -s, --server [host]          skip menu, and connect to the host if specified or to the first host in your preferences.\n"
02224             << "  -m, --multiplayer            runs a multiplayer game. There are additional\n"
02225             << "                               options that can be used as explained below:\n"
02226             << "  --algorithm<number>=value    selects a non-standard algorithm to be used by the\n"
02227             << "                               AI controller for this side.\n"
02228             << "  --controller<number>=value   selects the controller for this side.\n"
02229             << "  --era=value                  selects the era to be played in by its id.\n"
02230             << "  --nogui                      runs the game without the GUI. Must appear before\n"
02231             << "                               --multiplayer to have the desired effect.\n"
02232             << "  --parm<number>=name:value    sets additional parameters for this side.\n"
02233             << "  --scenario=value             selects a multiplayer scenario. The default\n"
02234             << "                               scenario is \"multiplayer_The_Freelands\".\n"
02235             << "  --side<number>=value         selects a faction of the current era for this side\n"
02236             << "                               by id.\n"
02237             << "  --turns=value                sets the number of turns. The default is \"50\".\n"
02238             << "  --exit-at-end                exit Wesnoth at end of scenario.\n"
02239             << "  --new-widgets                there is a new WIP widget toolkit this switch enables the new toolkit\n"
02240             << "                               (VERY EXPERIMENTAL don't file bug reports since most are known).\n"
02241             ;
02242             return 0;
02243         } else if(val == "--version" || val == "-v") {
02244             std::cout << _("Battle for Wesnoth") << " " << game_config::version
02245                       << "\n";
02246             return 0;
02247         } else if(val == "--path") {
02248             std::cout <<  game_config::path
02249                       << "\n";
02250             return 0;
02251 #ifdef HAVE_PYTHON
02252         } else if(val == "--python-api") {
02253             python_ai::invoke("documentation.py");
02254             return 0;
02255 #endif
02256         } else if (val.substr(0, 6) == "--log-") {
02257             size_t p = val.find('=');
02258             if (p == std::string::npos) {
02259                 std::cerr << "unknown option: " << val << '\n';
02260                 return 0;
02261             }
02262             std::string s = val.substr(6, p - 6);
02263             int severity;
02264             if (s == "error") severity = 0;
02265             else if (s == "warning") severity = 1;
02266             else if (s == "info") severity = 2;
02267             else if (s == "debug") severity = 3;
02268             else {
02269                 std::cerr << "unknown debug level: " << s << '\n';
02270                 return 0;
02271             }
02272             while (p != std::string::npos) {
02273                 size_t q = val.find(',', p + 1);
02274                 s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
02275                 if (!lg::set_log_domain_severity(s, severity)) {
02276                     std::cerr << "unknown debug domain: " << s << '\n';
02277                     return 0;
02278                 }
02279                 p = q;
02280             }
02281         //! @todo the (de)compress will be removed, the feature is broken for quite
02282         //! a while and is replaced with --g(un)zip. 1.5.1 would be a nice point for
02283         //! removal.
02284         } else if(val == "--compress" || val == "--decompress") {
02285             if(argc != arg+3) {
02286                 std::cerr << "format of " << val << " command: " << val << " <input file> <output file>\n";
02287                 return 0;
02288             }
02289 
02290             const std::string input(argv[arg+1]);
02291             const std::string output(argv[arg+2]);
02292 
02293             scoped_istream stream = istream_file(input);
02294             if (stream->fail()) {
02295                 std::cerr << "could not read file '" << input << "'\n";
02296                 return 0;
02297             }
02298 
02299             config cfg;
02300 
02301             const bool compress = val == "--compress";
02302             try {
02303                 const bool is_compressed = detect_format_and_read(cfg, *stream);
02304                 if(is_compressed && compress) {
02305                     std::cerr << input << " is already compressed\n";
02306                     return 0;
02307                 } else if(!is_compressed && !compress) {
02308                     std::cerr << input << " is already decompressed\n";
02309                     return 0;
02310                 }
02311 
02312                 scoped_ostream output_stream = ostream_file(output);
02313                 write_possibly_compressed(*output_stream, cfg, compress);
02314             } catch(config::error& e) {
02315                 std::cerr << input << " is not a valid Wesnoth file: " << e.message << "\n";
02316             } catch(io_exception& e) {
02317                 std::cerr << "IO error: " << e.what() << "\n";
02318             }
02319 
02320             return 0;
02321 
02322         } else if(val == "--gzip") {
02323             if(argc != arg + 2) {
02324                 std::cerr << "format of " << val << " command: " << val << " <input file>\n";
02325                 return 2;
02326             }
02327 
02328             const std::string input_file(argv[arg + 1]);
02329             const std::string output_file(input_file + ".gz");
02330             gzip_encode(input_file, output_file); 
02331 
02332         } else if(val == "--gunzip") {
02333             if(argc != arg + 2) {
02334                 std::cerr << "format of " << val << " command: " << val << " <input file>\n";
02335                 return 2;
02336             }
02337 
02338             const std::string input_file(argv[arg + 1]);
02339             if(! is_gzip_file(input_file)) {
02340 
02341                 std::cerr << "file '" << input_file << "'isn't a .gz file\n";
02342                 return 2;
02343             }
02344             const std::string output_file(
02345                 input_file, 0, input_file.length() - 3);
02346 
02347             gzip_decode(input_file, output_file);
02348 
02349         } else if(val == "--logdomains") {
02350             std::cout << lg::list_logdomains() << "\n";
02351             return 0;
02352         }
02353 
02354     }
02355 
02356     srand(time(NULL));
02357 
02358     game_controller game(argc,argv);
02359     const int start_ticks = SDL_GetTicks();
02360 
02361     // I would prefer to setup locale first so that early error
02362     // messages can get localized, but we need the game_controller
02363     // initialized to have get_intl_dir() to work.  Note: this
02364     // setlocale() but this does not take GUI language setting
02365     // into account.
02366     std::setlocale(LC_ALL, "C");
02367     std::setlocale(LC_MESSAGES, "");
02368     const std::string& intl_dir = get_intl_dir();
02369     bindtextdomain (PACKAGE, intl_dir.c_str());
02370     bind_textdomain_codeset (PACKAGE, "UTF-8");
02371     bindtextdomain (PACKAGE "-lib", intl_dir.c_str());
02372     bind_textdomain_codeset (PACKAGE "-lib", "UTF-8");
02373     textdomain (PACKAGE);
02374 
02375     bool res;
02376 
02377     // do initialize fonts before reading the game config, to have game
02378     // config error messages displayed. fonts will be re-initialized later
02379     // when the language is read from the game config.
02380     res = font::load_font_config();
02381     if(res == false) {
02382         std::cerr << "could not initialize fonts\n";
02383         return 0;
02384     }
02385 
02386     res = game.init_video();
02387     if(res == false) {
02388         std::cerr << "could not initialize display\n";
02389         return 0;
02390     }
02391 
02392     const cursor::manager cursor_manager;
02393     cursor::set(cursor::WAIT);
02394 
02395     loadscreen::global_loadscreen = new loadscreen(game.disp().video());
02396     loadscreen::global_loadscreen->clear_screen();
02397 
02398     res = game.init_language();
02399     if(res == false) {
02400         std::cerr << "could not initialize the language\n";
02401         return 0;
02402     }
02403 
02404     loadscreen::global_loadscreen->increment_progress(5, _("Loading game configuration."));
02405     res = game.init_config();
02406     if(res == false) {
02407         std::cerr << "could not initialize game config\n";
02408         return 0;
02409     }
02410     loadscreen::global_loadscreen->increment_progress(10, _("Re-initialize fonts for the current language."));
02411 
02412     res = font::load_font_config();
02413     if(res == false) {
02414         std::cerr << "could not re-initialize fonts for the current language\n";
02415         return 0;
02416     }
02417 
02418 #if defined(_X11) && !defined(__APPLE__)
02419     SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
02420 #endif
02421 
02422     config tips_of_day;
02423 
02424     loadscreen::global_loadscreen->set_progress(100, _("Loading title screen."));
02425     delete loadscreen::global_loadscreen;
02426     loadscreen::global_loadscreen = NULL;
02427 
02428     LOG_CONFIG << "time elapsed: "<<  (SDL_GetTicks() - start_ticks) << " ms\n";
02429 
02430     bool redraw_background = true;
02431 
02432     for(int first_time = true;;first_time = false){
02433         //init_config already processed the configs, so we don't need to do it for the
02434         //first loop pass.
02435         if (!first_time){
02436             //make sure the game config is always set to how it should be at the title screen
02437             //game.reset_game_cfg();
02438         }
02439 
02440         // reset the TC, since a game can modify it, and it may be used
02441         // by images in add-ons or campaigns dialogs
02442         image::set_team_colors();
02443 
02444         statistics::fresh_stats();
02445 
02446         sound::play_music_repeatedly(game_config::title_music);
02447 
02448         if(game.play_test() == false) {
02449             return 0;
02450         }
02451 
02452         if(game.play_multiplayer_mode() == false) {
02453             return 0;
02454         }
02455 
02456         recorder.clear();
02457 
02458         //Start directly a campaign
02459         if(game.goto_campaign() == false){
02460             continue; //Go to main menu
02461         }
02462 
02463         //Start directly a multiplayer
02464         //Eventually with a specified server
02465         if(game.goto_multiplayer() == false){
02466             continue; //Go to main menu
02467         }
02468 
02469         gui::TITLE_RESULT res = game.is_loading() ? gui::LOAD_GAME : gui::NOTHING;
02470 
02471         while(res == gui::NOTHING) {
02472             res = gui::show_title(game.disp(),tips_of_day, redraw_background);
02473             if (res == gui::REDRAW_BACKGROUND) {
02474                 redraw_background = true;
02475                 res = gui::NOTHING;
02476             } else {
02477                 redraw_background = false;
02478             }
02479         }
02480 
02481         game_controller::RELOAD_GAME_DATA should_reload = game_controller::RELOAD_DATA;
02482 
02483         if(res == gui::QUIT_GAME) {
02484             LOG_GENERAL << "quitting game...\n";
02485             return 0;
02486         } else if(res == gui::LOAD_GAME) {
02487             if(game.load_game() == false) {
02488                 continue;
02489             }
02490 
02491             should_reload = game_controller::NO_RELOAD_DATA;
02492         } else if(res == gui::TUTORIAL) {
02493             game.set_tutorial();
02494         } else if(res == gui::NEW_CAMPAIGN) {
02495             if(game.new_campaign() == false) {
02496                 continue;
02497             }
02498         } else if(res == gui::MULTIPLAYER) {
02499             if(game.play_multiplayer() == false) {
02500                 // Need a redraw because we can left the lobby without playing.
02501                 // (the redraw is only useless when canceling the multiplayer dialog)
02502                 // FIXME: game.play_multiplayer() always return false (why?),
02503                 // perhaps change this to identify real "cancel" cases?
02504                 redraw_background = true;
02505                 continue;   
02506             }
02507         } else if(res == gui::CHANGE_LANGUAGE) {
02508             if(game.change_language() == true) {
02509                 tips_of_day.clear();
02510             }
02511             continue;
02512         } else if(res == gui::EDIT_PREFERENCES) {
02513             game.show_preferences();
02514             if (game.disp().video().modeChanged()) {
02515                 redraw_background = true;
02516             }
02517             continue;
02518         } else if(res == gui::SHOW_ABOUT) {
02519             about::show_about(game.disp());
02520             continue;
02521         } else if(res == gui::SHOW_HELP) {
02522             help::help_manager help_manager(&game.game_config(), NULL);
02523             help::show_help(game.disp());
02524             continue;
02525         } else if(res == gui::GET_ADDONS) {
02526             game.manage_addons();
02527             continue;
02528         } else if(res == gui::BEG_FOR_UPLOAD) {
02529             game.show_upload_begging();
02530             continue;
02531 #ifdef MAP_EDITOR
02532         } else if(res == gui::START_MAP_EDITOR) {
02533             gui::show_error_message(game.disp(), "The map editor is not available. Yet.");
02534             //NOTE: will probably need a "redraw_background = true";
02535             continue;
02536 #endif
02537         }
02538 
02539         if (recorder.at_end()){
02540             game.play_game(should_reload);
02541         }
02542         else{
02543             game.play_replay();
02544         }
02545 
02546         // We played something, refresh background
02547         redraw_background = true;
02548     }
02549 
02550     return 0;
02551 }
02552 
02553 int main(int argc, char** argv)
02554 {
02555 #ifdef OS2 /* required for SDL_GetTicks to work on OS/2 */
02556         if(SDL_Init(SDL_INIT_TIMER) < 0) {
02557         fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
02558         return(1);
02559     }
02560 #endif
02561 
02562     try {
02563         std::cerr << "Battle for Wesnoth v" << game_config::revision << '\n';
02564         time_t t = time(NULL);
02565         std::cerr << "Started on " << ctime(&t) << "\n";
02566 
02567         const int res = play_game(argc,argv);
02568         safe_exit(res);
02569     } catch(CVideo::error&) {
02570         std::cerr << "Could not initialize video. Exiting.\n";
02571     } catch(font::manager::error&) {
02572         std::cerr << "Could not initialize fonts. Exiting.\n";
02573     } catch(config::error& e) {
02574         std::cerr << e.message << "\n";
02575     } catch(gui::button::error&) {
02576         std::cerr << "Could not create button: Image could not be found\n";
02577     } catch(CVideo::quit&) {
02578         //just means the game should quit
02579     } catch(end_level_exception&) {
02580         std::cerr << "caught end_level_exception (quitting)\n";
02581     } catch(std::bad_alloc&) {
02582         std::cerr << "Ran out of memory. Aborted.\n";
02583     } catch(twml_exception& e) {
02584         std::cerr << "WML exception:\nUser message: "
02585             << e.user_message << "\nDev message: " << e.dev_message << '\n';
02586     }
02587 
02588     return 0;
02589 } // end main
02590 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs