1 /******************************************************************************
2  *  Warmux is a convivial mass murder game.
3  *  Copyright (C) 2001-2011 Warmux Team.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  ******************************************************************************
19  * Warmux configuration : all variables interesting to tweak should be here.
20  * A default value is affected for each variable, the value can be changed by
21  * the configuration file.
22  *****************************************************************************/
23 
24 #include <cstdlib>
25 #include <sstream>
26 #include <string>
27 #include <iostream>
28 #include <errno.h>
29 #include <libxml/tree.h>
30 
31 #ifdef __APPLE__
32 #  include <CoreFoundation/CoreFoundation.h>
33 #endif
34 
35 #ifdef WIN32
36 #  include <windows.h>
37 #  include <direct.h>
38 #endif
39 
40 #include <WARMUX_file_tools.h>
41 #include <WARMUX_team_config.h>
42 
43 #include "game/config.h"
44 #include "game/game.h"
45 #include "graphic/font.h"
46 #include "graphic/video.h"
47 #include "include/app.h"
48 #include "include/constant.h"
49 #include "interface/keyboard.h"
50 #include "network/network.h"
51 #include "object/object_cfg.h"
52 #include "sound/jukebox.h"
53 #include "team/team.h"
54 #include "team/teams_list.h"
55 #include "tool/resource_manager.h"
56 #include "tool/string_tools.h"
57 #include "tool/xml_document.h"
58 #include "weapon/weapons_list.h"
59 
60 #ifdef _WIN32
61 // Under Windows, binary may be relocated
GetWarmuxPath()62 static std::string GetWarmuxPath()
63 {
64   WCHAR  buffer[4*MAX_PATH];
65   DWORD size = GetModuleFileNameW(NULL, buffer, 4*MAX_PATH);
66 
67   if (size<1)
68     return std::string("");
69 
70   // Now get shortname
71   size = GetShortPathNameW(buffer, NULL, 0);
72   ASSERT(size);
73   WCHAR *buf = new WCHAR[size];
74   GetShortPathNameW(buffer, buf, size);
75 
76   // Retrieve the path and convert it to ANSI
77   size = wcsrchr((wchar_t*)buf, L'\\')+1 - buf;
78   ASSERT(size < MAX_PATH);
79   int ulen = WideCharToMultiByte(CP_UTF8, 0, buf, size, NULL, 0, NULL, NULL);
80 
81   std::string ret;
82   ret.resize(ulen-1);
83   WideCharToMultiByte(CP_UTF8, 0, buf, size, (LPSTR)ret.c_str(), ulen-1, NULL, NULL);
84   delete[] buf;
85 
86   return ret;
87 }
88 #else
89 #  if defined(ANDROID)
GetWarmuxPath()90 static std::string GetWarmuxPath() { return "."; }
91 #  elif defined(GEKKO)
GetWarmuxPath()92 static std::string GetWarmuxPath() { return "sd:/apps/Warmux"; }
93 #  endif
94 #  include <unistd.h> // not needed by mingw
95 #endif
96 
97 static const std::string FILENAME="config.xml";
98 
Config()99 Config::Config()
100   : default_language("")
101   , m_game_mode("classic")
102   , display_energy_character(true)
103   , display_name_character(true)
104 
105 #ifdef HAVE_HANDHELD
106   , wind_particles_percentage(0) // Too CPU intensive
107   , display_multi_layer_sky(false) // Memory hungry + CPU intensive
108 #else
109   , wind_particles_percentage(100)
110   , display_multi_layer_sky(true)
111 #endif
112 
113   , default_mouse_cursor(false)
114   , video_width(0)
115   , video_height(0)
116 
117 #ifdef HAVE_TOUCHSCREEN
118   , video_fullscreen(true) // No other mode supported
119   , max_fps(25)
120 #else
121   , video_fullscreen(false)
122   , max_fps(50)
123 #endif
124 
125   , bling_bling_interface(false)
126   , scroll_on_border(false)
127   , scroll_border_size(50)
128   , sound_music(true)
129   , sound_effects(true)
130 #ifdef HAVE_HANDHELD
131   , sound_frequency(22050)
132 #else
133   , sound_frequency(44100)
134 #endif
135   , warn_on_new_player(true)
136   , check_updates(false)
137   , lefthanded_mouse(false)
138   , m_network_client_host("localhost")
139   , m_network_client_port(WARMUX_NETWORK_PORT)
140   , m_network_server_game_name("Warmux party")
141   , m_network_server_port(WARMUX_NETWORK_PORT)
142   , m_network_server_public(true)
143 
144 #ifdef HAVE_HANDHELD
145   , quality(QUALITY_16BPP)
146 #else
147   , quality(QUALITY_32BPP)
148 #endif
149 {
150   // Set audio volume
151   volume_music = JukeBox::GetMaxVolume()/2;
152   volume_effects = JukeBox::GetMaxVolume()/2;
153 
154   Constants::GetInstance();
155 
156   // directories
157 #if defined(__APPLE__)
158   // the following code will enable warmux to find its data when placed in an app bundle on mac OS X.
159   // configure with './configure ... CPPFLAGS=-DOSX_BUNDLE' to enable
160   char path[1024];
161   CFBundleRef mainBundle = CFBundleGetMainBundle(); assert(mainBundle);
162   CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle); assert(mainBundleURL);
163   CFStringRef cfStringRef = CFURLCopyFileSystemPath( mainBundleURL, kCFURLPOSIXPathStyle); assert(cfStringRef);
164   CFStringGetCString(cfStringRef, path, 1024, kCFStringEncodingASCII);
165   CFRelease(mainBundleURL);
166   CFRelease(cfStringRef);
167 
168   std::string contents = std::string(path) + std::string("/Contents");
169   if(contents.find(".app") != std::string::npos){
170       // executable is inside an app bundle, use app bundle-relative paths
171       std::string default_data_dir = contents + std::string("/Resources/data/");
172       std::string default_ttf_filename = contents + std::string("/Resources/data/font/Ubuntu-R.ttf");
173 
174       // if environment variables exist, they will override default values
175       data_dir     = GetEnv(Constants::ENV_DATADIR, default_data_dir);
176       ttf_filename = GetEnv(Constants::ENV_FONT_PATH, default_ttf_filename);
177       personal_config_dir = GetHome() + "/Library/Application Support/Warmux/";
178       personal_data_dir = personal_config_dir;
179 #  ifdef ENABLE_NLS
180       std::string default_locale_dir = contents + std::string("/Resources/locale/");
181       locale_dir   = GetEnv(Constants::ENV_LOCALEDIR, default_locale_dir);
182 #  endif
183   }
184   else {
185       // executable is installed Unix-style, use default paths
186       data_dir     = GetEnv(Constants::ENV_DATADIR, INSTALL_DATADIR);
187 #  ifdef ENABLE_NLS
188       locale_dir   = GetEnv(Constants::ENV_LOCALEDIR, INSTALL_LOCALEDIR);
189 #  endif
190       ttf_filename = GetEnv(Constants::ENV_FONT_PATH, FONT_FILE);
191   }
192 #elif defined(_WIN32) || defined(ANDROID) || defined(GEKKO)
193   std::string basepath = GetWarmuxPath();
194   data_dir     = basepath + PATH_SEPARATOR "data" PATH_SEPARATOR;
195 #  ifdef ENABLE_NLS
196   locale_dir   = basepath + PATH_SEPARATOR "locale" PATH_SEPARATOR;
197 #  endif
198   ttf_filename = basepath + PATH_SEPARATOR FONT_FILE;
199   font_dir     = data_dir + PATH_SEPARATOR "font" PATH_SEPARATOR;
200 
201   personal_config_dir = GetHome() + PATH_SEPARATOR "Warmux" PATH_SEPARATOR;
202   personal_data_dir = personal_config_dir;
203 
204 #else //Neither WIN32, ANDROID or __APPLE__
205   data_dir     = GetEnv(Constants::ENV_DATADIR, INSTALL_DATADIR);
206 #  ifdef ENABLE_NLS
207   locale_dir   = GetEnv(Constants::ENV_LOCALEDIR, INSTALL_LOCALEDIR);
208 #  endif
209   ttf_filename = GetEnv(Constants::ENV_FONT_PATH, FONT_FILE);
210   font_dir     = GetEnv(Constants::ENV_FONT_PATH, data_dir + PATH_SEPARATOR "font" PATH_SEPARATOR);
211 
212   // To respect XDG Base Directory Specification from FreeDesktop
213   // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
214 
215   const char * c_config_dir = std::getenv("XDG_CONFIG_HOME");
216   const char * c_data_dir = std::getenv("XDG_DATA_HOME");
217 
218   if (c_config_dir == NULL)
219     personal_config_dir = GetHome() + "/.config";
220   else
221     personal_config_dir = c_config_dir;
222 
223   personal_config_dir += "/wormux/";
224 
225   if (c_data_dir == NULL) {
226     personal_data_dir = GetHome() + "/.local/share";
227   }
228   else
229     personal_data_dir = c_data_dir;
230 
231   personal_data_dir += "/wormux/";
232 #endif
233 
234   std::string old_dir = GetOldPersonalDir();
235   if (old_dir != "") {
236     std::cout << "Moving " << old_dir << " to " << personal_data_dir << std::endl;
237     if (old_dir != personal_data_dir)
238       Rename(old_dir, personal_data_dir);
239   }
240 
241   std::string old_config_file_name = personal_data_dir + "config.xml";
242   std::string config_file_name = personal_config_dir + "config.xml";
243   if (old_config_file_name != config_file_name) {
244     std::cout << "Moving " << old_config_file_name
245               << " to " << config_file_name << std::endl;
246     Rename(old_config_file_name, config_file_name);
247   }
248 
249   chat_log_dir = personal_data_dir + "logs" PATH_SEPARATOR;
250 
251   // Create the directories
252   // Delayed after copy/creation of personal_data_dir, because they
253   // might cause an access denied (because of indexing under Windows)
254   MkdirChatLogDir();
255   MkdirPersonalConfigDir();
256   MkdirPersonalDataDir();
257 
258   LoadDefaultValue();
259 
260   // Load personal config
261   std::string dir;
262   if (!DoLoading())
263   {
264 #ifdef ENABLE_NLS
265     // Failed, still try to apply default config then
266     SetLanguage("");
267 #endif
268   }
269 
270   dir = TranslateDirectory(data_dir);
271   GetResourceManager().SetDataPath(dir + PATH_SEPARATOR);
272 }
273 
MkdirChatLogDir() const274 bool Config::MkdirChatLogDir() const
275 {
276   return CreateFolder(chat_log_dir);
277 }
278 
MkdirPersonalConfigDir() const279 bool Config::MkdirPersonalConfigDir() const
280 {
281   bool r = CreateFolder(personal_config_dir);
282 
283   if (r) {
284     CreateFolder(personal_config_dir + "custom_team");
285   }
286 
287   return r;
288 }
289 
MkdirPersonalDataDir() const290 bool Config::MkdirPersonalDataDir() const
291 {
292   bool r = CreateFolder(personal_data_dir);
293 
294   if (r) {
295     CreateFolder(personal_data_dir + "map");
296     CreateFolder(personal_data_dir + "team");
297     CreateFolder(personal_data_dir + "game_mode");
298   }
299 
300   return r;
301 }
302 
RemovePersonalConfigFile() const303 bool Config::RemovePersonalConfigFile() const
304 {
305   std::string personal_config_file = personal_config_dir + FILENAME;
306 
307   int r = unlink(personal_config_file.c_str());
308   if (r) {
309     if (errno == -ENOENT) {
310       r = 0;
311     } else {
312       perror((Format("Fail to remove personal config file %s", personal_config_file.c_str())).c_str());
313     }
314   }
315 
316   if (r)
317     return false;
318 
319   return true;
320 }
321 
322 #ifdef ENABLE_NLS
SetLanguage(const std::string & language)323 void Config::SetLanguage(const std::string& language)
324 {
325   default_language = language;
326   InitI18N(TranslateDirectory(locale_dir), language);
327 
328   Font::ReleaseInstances();
329   if (GameIsRunning()) {
330     Game::GetInstance()->UpdateTranslation();
331   }
332 }
333 #endif
334 
335 /*
336  * Load physics constants from the xml file and cache it.
337  * This tries to find already loaded data in the map<> config_set and actually
338  * load it if it cannot be found.
339  */
GetObjectConfig(const std::string & name,const std::string & xml_config) const340 const ObjectConfig &Config::GetObjectConfig(const std::string &name, const std::string &xml_config) const
341 {
342   ObjectConfig * objcfg;
343 
344   std::map<std::string, ObjectConfig*>::const_iterator  it = config_set.find(name);
345   if (it == config_set.end()) {
346     objcfg = new ObjectConfig();
347     objcfg->LoadXml(name,xml_config);
348     config_set[name] = objcfg;
349   } else {
350     objcfg = it->second;
351   }
352   return *objcfg;
353 }
354 
RemoveAllObjectConfigs()355 void Config::RemoveAllObjectConfigs()
356 {
357   std::map<std::string, ObjectConfig*>::iterator it = config_set.begin(),
358     end = config_set.end();
359 
360   while (it != end) {
361     delete (it->second);
362     config_set.erase(it);
363     it = config_set.begin();
364   }
365 }
366 
DoLoading(void)367 bool Config::DoLoading(void)
368 {
369   // create the directory if it does not exist (we should do it before exiting the game)
370   // the user can ask to start an internet game and download a file in the personnal dir
371   // so it should exist
372   MkdirPersonalDataDir();
373   MkdirPersonalConfigDir();
374 
375   // Load XML conf
376   XmlReader doc;
377 
378   m_filename = personal_config_dir + FILENAME;
379 
380   if (!doc.Load(m_filename))
381     return false;
382 
383   LoadXml(doc.GetRoot());
384   return true;
385 }
386 
LoadDefaultValue()387 void Config::LoadDefaultValue()
388 {
389   // Load default XML conf
390 #ifdef ANDROID
391   m_default_config = GetDataDir() + "warmux_default_android_config.xml";
392 #elif MAEMO
393   m_default_config = GetDataDir() + "warmux_default_maemo_config.xml";
394 #elif __SYMBIAN32__
395   m_default_config = GetDataDir() + "warmux_default_symbian_config.xml";
396 #else
397   m_default_config = GetDataDir() + "warmux_default_config.xml";
398 #endif
399   Profile *res = GetResourceManager().LoadXMLProfile(m_default_config, true);
400 
401   std::cout << "o " << _("Reading the default config file") << std::endl;
402   std::ostringstream section;
403   Point2i tmp;
404 
405   //=== Default video value ===
406   int number_of_resolution_available = GetResourceManager().LoadInt(res, "default_video_mode/number_of_resolution_available");
407   for(int i = 1; i <= number_of_resolution_available; i++) {
408     tmp = Point2i(0, 0);
409     std::ostringstream section; section << "default_video_mode/" << i;
410     tmp = GetResourceManager().LoadPoint2i(res, section.str());
411     if(tmp.GetX() > 0 && tmp.GetY() > 0)
412       resolution_available.push_back(tmp);
413   }
414 
415   //== Default keyboard key
416   const xmlNode *node = GetResourceManager().GetElement(res, "section", "default_keyboard_layout");
417   if (node) {
418     Keyboard::GetInstance()->SetConfig(node);
419   }
420 
421 #ifdef ENABLE_NLS
422   //=== Default fonts value ===
423   node = GetResourceManager().GetElement(res, "section", "default_language_fonts");
424   if (node) {
425     xmlNodeArray list = XmlReader::GetNamedChildren(node, "language");
426     for (xmlNodeArray::iterator it = list.begin(); it != list.end(); ++it) {
427       std::string lang, font;
428       if (res->doc->ReadStringAttr(*it, "name", lang) &&
429          res->doc->ReadStringAttr(*it, "file", font)) {
430         bool rel = false;
431         res->doc->ReadBoolAttr(*it, "relative", rel);
432         fonts[lang] = (rel) ? font_dir + font : font;
433         //std::cout << "Language " << lang << ": " << fonts[lang] << std::endl;
434       }
435     }
436   }
437 #endif
438 
439 #if 0 //== Team Color
440   int number_of_team_color = GetResourceManager().LoadInt(res, "team_colors/number_of_team_color");
441   for(int i = 1; i <= number_of_team_color; i++) {
442     tmp = Point2i(0, 0);
443     std::ostringstream section; section << "team_colors/" << i;
444     tmp = GetResourceManager().LoadPoint2i(res, section.str());
445     if(tmp.GetX() > 0 && tmp.GetY() > 0)
446       resolution_available.push_back(tmp);
447   }
448 #endif
449 
450   GetResourceManager().UnLoadXMLProfile(res);
451 }
452 
453 // Read personal config file
LoadXml(const xmlNode * xml)454 void Config::LoadXml(const xmlNode *xml)
455 {
456   const xmlNode *elem;
457 
458   std::cout << "o " << _("Reading the personal config file") << std::endl;
459 
460   //=== Map ===
461   XmlReader::ReadString(xml, "map", map_name);
462 
463   //=== Language ===
464   XmlReader::ReadString(xml, "default_language", default_language);
465 #ifdef ENABLE_NLS
466   SetLanguage(default_language);
467 #endif
468 
469   //=== Teams ===
470   if ((elem = XmlReader::GetMarker(xml, "teams"))) {
471     int i = 0;
472 
473     const xmlNode *team;
474 
475     while ((team = XmlReader::GetMarker(elem, "team_" + int2str(i)))) {
476       ConfigTeam one_team;
477       XmlReader::ReadString(team, "id", one_team.id);
478       XmlReader::ReadString(team, "player_name", one_team.player_name);
479       XmlReader::ReadUint(team, "nb_characters", one_team.nb_characters);
480       // The ai element needs a defaut as it has been added afterwards:
481       if (!XmlReader::ReadString(team, "ai", one_team.ai)) {
482         one_team.ai = (i == 1) ? DEFAULT_AI_NAME : NO_AI_NAME;
483       }
484 
485       teams.push_back(one_team);
486 
487       // get next team
488       i++;
489     }
490   }
491 
492   //=== Video ===
493   if ((elem = XmlReader::GetMarker(xml, "video"))) {
494     XmlReader::ReadBool(elem, "bling_bling_interface", bling_bling_interface);
495     XmlReader::ReadUint(elem, "max_fps", max_fps);
496     XmlReader::ReadUint(elem, "wind_particles_percentage", wind_particles_percentage);
497     if (wind_particles_percentage > 100)
498       wind_particles_percentage = 100;
499     XmlReader::ReadBool(elem, "display_multi_layer_sky", display_multi_layer_sky);
500     XmlReader::ReadBool(elem, "display_energy_character", display_energy_character);
501     XmlReader::ReadBool(elem, "display_name_character", display_name_character);
502     XmlReader::ReadBool(elem, "default_mouse_cursor", default_mouse_cursor);
503 #ifndef HAVE_TOUCHSCREEN // Those should never be set
504     XmlReader::ReadBool(elem, "scroll_on_border", scroll_on_border);
505     XmlReader::ReadUint(elem, "scroll_border_size", scroll_border_size);
506 #endif
507     XmlReader::ReadUint(elem, "width", video_width);
508     XmlReader::ReadUint(elem, "height", video_height);
509     XmlReader::ReadBool(elem, "full_screen", video_fullscreen);
510 
511     uint qual;
512     if (XmlReader::ReadUint(elem, "quality", qual)) {
513 	if (qual>QUALITY_MAX-1) qual=QUALITY_MAX-1;
514 	quality = (Quality)qual;
515     }
516   }
517 
518   //=== Sound ===
519   if ((elem = XmlReader::GetMarker(xml, "sound"))) {
520     XmlReader::ReadBool(elem, "music", sound_music);
521     XmlReader::ReadBool(elem, "effects", sound_effects);
522 #ifndef HAVE_HANDHELD
523     XmlReader::ReadUint(elem, "frequency", sound_frequency);
524 #endif
525     XmlReader::ReadUint(elem, "volume_music", volume_music);
526     XmlReader::ReadUint(elem, "volume_effects", volume_effects);
527   }
528 
529   //=== network ===
530   if ((elem = XmlReader::GetMarker(xml, "network"))) {
531     const xmlNode *sub_elem;
532     if ((sub_elem = XmlReader::GetMarker(elem, "as_client"))) {
533       XmlReader::ReadString(sub_elem, "host", m_network_client_host);
534       XmlReader::ReadString(sub_elem, "port", m_network_client_port);
535     }
536 
537     if ((sub_elem = XmlReader::GetMarker(elem, "as_server"))) {
538       XmlReader::ReadString(sub_elem, "game_name", m_network_server_game_name);
539       XmlReader::ReadString(sub_elem, "port", m_network_server_port);
540       XmlReader::ReadBool(sub_elem, "public", m_network_server_public);
541     }
542 
543     //=== personal teams used in last network game ===
544     if ((sub_elem = XmlReader::GetMarker(elem, "local_teams"))) {
545       int i = 0;
546       const xmlNode *team;
547 
548       while ((team = XmlReader::GetMarker(sub_elem, "team_" + int2str(i)))) {
549         ConfigTeam one_team;
550         XmlReader::ReadString(team, "id", one_team.id);
551         XmlReader::ReadString(team, "player_name", one_team.player_name);
552         XmlReader::ReadUint(team, "nb_characters", one_team.nb_characters);
553         // The ai element needs a defaut as it has been added afterwards:
554         if (!XmlReader::ReadString(team, "ai", one_team.ai)) {
555           one_team.ai = NO_AI_NAME;
556         }
557 
558         network_local_teams.push_back(one_team);
559 
560         // get next team
561         i++;
562       }
563     }
564   }
565 
566   //=== misc ===
567   if ((elem = XmlReader::GetMarker(xml, "misc"))) {
568     XmlReader::ReadBool(elem, "check_updates", check_updates);
569     XmlReader::ReadBool(elem, "left-handed_mouse", lefthanded_mouse);
570   }
571 
572   //=== game mode ===
573   XmlReader::ReadString(xml, "game_mode", m_game_mode);
574 
575   //=== controls ===
576   if ((elem = XmlReader::GetMarker(xml, "controls"))) {
577     const xmlNode *node = XmlReader::GetMarker(elem, "keyboard");
578     if (node) {
579       Keyboard::GetInstance()->SetConfig(node);
580     }
581   }
582 }
583 
Save(bool save_current_teams)584 bool Config::Save(bool save_current_teams)
585 {
586   std::string rep = personal_config_dir;
587 
588   // Create the directory if it doesn't exist
589   if (!MkdirPersonalConfigDir())
590   {
591     std::cerr << "o "
592               << Format(_("Error while creating directory \"%s\": unable to store configuration file."),
593                         rep.c_str())
594               << " " << strerror(errno)
595               << std::endl;
596     return false;
597   }
598 
599   return SaveXml(save_current_teams);
600 }
601 
SaveXml(bool save_current_teams)602 bool Config::SaveXml(bool save_current_teams)
603 {
604   XmlWriter doc;
605 
606   doc.Create(m_filename, "config", "1.0", "utf-8");
607   xmlNode *root = doc.GetRoot();
608   doc.WriteElement(root, "version", Constants::WARMUX_VERSION);
609 
610   //=== Map ===
611   //The map name is modified when the player validate its choice in the
612   //map selection box.
613   doc.WriteElement(root, "map", map_name);
614 
615   //=== Language ==
616   doc.WriteElement(root, "default_language", default_language);
617 
618   //=== Teams ===
619   xmlNode *team_elements = xmlAddChild(root,
620                                        xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"teams"));
621 
622   if (TeamsList::IsLoaded()) {
623     if (save_current_teams) {
624       teams.clear();
625 
626       TeamsList::iterator
627         it = GetTeamsList().playing_list.begin(),
628         end = GetTeamsList().playing_list.end();
629 
630       for (int i=0; it!=end; ++it, i++) {
631         ConfigTeam config;
632         config.id = (**it).GetId();
633         config.player_name = (**it).GetPlayerName();
634         config.nb_characters = (**it).GetNbCharacters();
635         config.ai = (**it).GetAIName();
636 
637         teams.push_back(config);
638       }
639     }
640 
641     std::list<ConfigTeam>::iterator
642       it = teams.begin(),
643       end = teams.end();
644 
645     for (int i=0; it!=end; ++it, i++) {
646        std::string name = "team_"+int2str(i);
647        xmlNode* a_team = xmlAddChild(team_elements,
648                                      xmlNewNode(NULL /* empty prefix */, (const xmlChar*)name.c_str()));
649        doc.WriteElement(a_team, "id", (*it).id);
650        doc.WriteElement(a_team, "player_name", (*it).player_name);
651        doc.WriteElement(a_team, "nb_characters", uint2str((*it).nb_characters));
652        doc.WriteElement(a_team, "ai", (*it).ai);
653     }
654   }
655 
656   //=== Video ===
657   Video * video = AppWarmux::GetInstance()->video;
658   xmlNode* video_node = xmlAddChild(root, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"video"));
659   doc.WriteElement(video_node, "wind_particles_percentage", uint2str(wind_particles_percentage));
660   doc.WriteElement(video_node, "display_multi_layer_sky", bool2str(display_multi_layer_sky));
661   doc.WriteElement(video_node, "display_energy_character", bool2str(display_energy_character));
662   doc.WriteElement(video_node, "display_name_character", bool2str(display_name_character));
663   doc.WriteElement(video_node, "bling_bling_interface", bool2str(bling_bling_interface));
664   doc.WriteElement(video_node, "default_mouse_cursor", bool2str(default_mouse_cursor));
665   doc.WriteElement(video_node, "scroll_on_border", bool2str(scroll_on_border));
666   doc.WriteElement(video_node, "scroll_border_size", uint2str(scroll_border_size));
667   doc.WriteElement(video_node, "width", uint2str(video->window.GetWidth()));
668   doc.WriteElement(video_node, "height", uint2str(video->window.GetHeight()));
669   doc.WriteElement(video_node, "full_screen", bool2str(video->IsFullScreen()));
670   doc.WriteElement(video_node, "quality", uint2str(quality));
671   doc.WriteElement(video_node, "max_fps", uint2str(video->GetMaxFps()));
672 
673   //=== Sound ===
674   xmlNode *sound_node = xmlAddChild(root, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"sound"));
675   doc.WriteElement(sound_node, "music",  bool2str(sound_music));
676   doc.WriteElement(sound_node, "effects", bool2str(sound_effects));
677 #ifndef HAVE_HANDHELD
678   doc.WriteElement(sound_node, "frequency", uint2str(sound_frequency));
679 #endif
680   doc.WriteElement(sound_node, "volume_music",  uint2str(volume_music));
681   doc.WriteElement(sound_node, "volume_effects", uint2str(volume_effects));
682 
683   //=== Network ===
684   xmlNode *net_node = xmlAddChild(root, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"network"));
685 
686   // Network as client parameters
687   xmlNode *net_as_client_node = xmlAddChild(net_node, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"as_client"));
688   doc.WriteElement(net_as_client_node, "host", m_network_client_host);
689   doc.WriteElement(net_as_client_node, "port", m_network_client_port);
690 
691   // Network as server parameters
692   xmlNode *net_as_server_node = xmlAddChild(net_node, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"as_server"));
693   doc.WriteElement(net_as_server_node, "game_name", m_network_server_game_name);
694   doc.WriteElement(net_as_server_node, "port", m_network_server_port);
695   doc.WriteElement(net_as_server_node, "public", bool2str(m_network_server_public));
696 
697   // personal teams used durint last network game
698   xmlNode *net_teams = xmlAddChild(net_node, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"local_teams"));
699   std::list<ConfigTeam>::iterator
700     it = network_local_teams.begin(),
701     end = network_local_teams.end();
702 
703   for (int i=0; it != end; ++it, i++) {
704     std::string name = "team_"+int2str(i);
705     xmlNode* a_team = xmlAddChild(net_teams,
706                                   xmlNewNode(NULL /* empty prefix */, (const xmlChar*)name.c_str()));
707     doc.WriteElement(a_team, "id", (*it).id);
708     doc.WriteElement(a_team, "player_name", (*it).player_name);
709     doc.WriteElement(a_team, "nb_characters", uint2str((*it).nb_characters));
710     doc.WriteElement(a_team, "ai", (*it).ai);
711   }
712 
713   //=== Misc ===
714   xmlNode *misc_node = xmlAddChild(root, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"misc"));
715   doc.WriteElement(misc_node, "check_updates", bool2str(check_updates));
716   doc.WriteElement(misc_node, "left-handed_mouse", bool2str(lefthanded_mouse));
717 
718   //=== game mode ===
719   doc.WriteElement(root, "game_mode", m_game_mode);
720 
721   //=== controls ===
722   xmlNode *controls_node = xmlAddChild(root, xmlNewNode(NULL /* empty prefix */, (const xmlChar*)"controls"));
723   Keyboard::GetInstance()->SaveConfig(controls_node);
724 
725   return doc.Save();
726 }
727 
728 /*
729  * Return the value of the environment variable 'name' or
730  * 'default' if not set
731  */
GetEnv(const std::string & name,const std::string & default_value) const732 std::string Config::GetEnv(const std::string & name, const std::string &default_value) const
733 {
734   const char *env = std::getenv(name.c_str());
735   return (env) ? env : default_value;
736 }
737 
SetVolumeMusic(uint vol)738 void Config::SetVolumeMusic(uint vol)
739 {
740   volume_music = vol;
741   JukeBox::SetMusicVolume(vol);
742 }
743 
GetMaxVolume()744 uint Config::GetMaxVolume()
745 {
746   return JukeBox::GetMaxVolume();
747 }
748 
GetTtfFilename() const749 const std::string& Config::GetTtfFilename() const
750 {
751 #ifdef ENABLE_NLS
752   if (fonts.find(default_language) == fonts.end())
753     return ttf_filename;
754   else {
755     std::map<std::string, std::string>::const_iterator it = fonts.find(default_language);
756     ASSERT(it != fonts.end());
757     return it->second;
758   }
759 #else
760   return ttf_filename;
761 #endif
762 }
763 
SetNetworkLocalTeams()764 void Config::SetNetworkLocalTeams()
765 {
766   // personal teams used durint last network game
767   network_local_teams.clear();
768 
769   TeamsList::iterator
770     it = GetTeamsList().playing_list.begin(),
771     end = GetTeamsList().playing_list.end();
772 
773   for (int i=0; it != end; ++it, i++) {
774     if ((**it).IsLocal()) {
775       ConfigTeam config;
776       config.id = (**it).GetId();
777       config.player_name = (**it).GetPlayerName();
778       config.nb_characters = (**it).GetNbCharacters();
779       config.ai = (**it).GetAIName();
780       network_local_teams.push_back(config);
781     }
782   }
783 }
784