The Battle for Wesnoth  1.15.5+dev
info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012 - 2018 by Iris Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
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  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "addon/info.hpp"
16 
17 #include "addon/manager.hpp"
18 #include "config.hpp"
19 #include "font/pango/escape.hpp"
20 #include "gettext.hpp"
21 #include "picture.hpp"
22 #include "log.hpp"
24 
25 #include <boost/locale.hpp>
26 
27 static lg::log_domain log_addons_client("addons-client");
28 #define ERR_AC LOG_STREAM(err , log_addons_client)
29 #define LOG_AC LOG_STREAM(info, log_addons_client)
30 
31 namespace {
32  void resolve_deps_recursive(const addons_list& addons, const std::string& base_id, std::set<std::string>& dest)
33  {
34  addons_list::const_iterator it = addons.find(base_id);
35  if(it == addons.end()) {
36  LOG_AC << "resolve_deps_recursive(): " << base_id << " not in add-ons list\n";
37  return;
38  }
39 
40  const std::vector<std::string>& base_deps = it->second.depends;
41 
42  if(base_deps.empty()) {
43  return;
44  }
45 
46  for(const std::string& dep : base_deps) {
47  if(base_id == dep) {
48  LOG_AC << dep << " depends upon itself; breaking circular dependency\n";
49  continue;
50  } else if(dest.find(dep) != dest.end()) {
51  LOG_AC << dep << " already in dependency tree; breaking circular dependency\n";
52  continue;
53  }
54 
55  dest.insert(dep);
56 
57  resolve_deps_recursive(addons, dep, dest);
58  }
59  }
60 }
61 
63 {
64  this->supported = cfg["supported"].to_bool(true);
65  this->title = cfg["title"].str();
66  this->description = cfg["description"].str();
67 }
68 
70 {
71  cfg["supported"] = this->supported;
72  cfg["title"] = this->title;
73  cfg["description"] = this->description;
74 }
75 
76 void addon_info::read(const config& cfg)
77 {
78  this->id = cfg["name"].str();
79  this->title = cfg["title"].str();
80  this->description = cfg["description"].str();
81  this->icon = cfg["icon"].str();
82  this->version = cfg["version"].str();
83  this->author = cfg["author"].str();
84  this->size = cfg["size"];
85  this->downloads = cfg["downloads"];
86  this->uploads = cfg["uploads"];
87  this->type = get_addon_type(cfg["type"].str());
88 
89  const config::const_child_itors& locales_as_configs = cfg.child_range("translation");
90 
91  for(const config& locale : locales_as_configs) {
92  if(locale["supported"].to_bool(true))
93  this->locales.emplace_back(locale["language"].str());
94  this->info_translations.emplace(locale["language"].str(), addon_info_translation(locale));
95  }
96 
97  this->core = cfg["core"].str();
98  this->depends = utils::split(cfg["dependencies"].str());
99  this->tags = utils::split(cfg["tags"].str());
100  this->feedback_url = cfg["feedback_url"].str();
101 
102  this->updated = cfg["timestamp"].to_time_t();
103  this->created = cfg["original_timestamp"].to_time_t();
104 
105  this->local_only = cfg["local_only"].to_bool();
106 }
107 
108 void addon_info::write(config& cfg) const
109 {
110  cfg["id"] = this->id;
111  cfg["title"] = this->title;
112  cfg["description"] = this->description;
113  cfg["icon"] = this->icon;
114  cfg["version"] = this->version.str();
115  cfg["author"] = this->author;
116  cfg["size"] = this->size;
117  cfg["downloads"] = this->downloads;
118  cfg["uploads"] = this->uploads;
119  cfg["type"] = get_addon_type_string(this->type);
120 
121  for(const auto& element : this->info_translations) {
122  config& locale = cfg.add_child("translation");
123  locale["language"] = element.first;
124  element.second.write(locale);
125  }
126 
127  cfg["core"] = this->core;
128  cfg["dependencies"] = utils::join(this->depends);
129  cfg["tags"] = utils::join(this->tags);
130  cfg["feedback_url"] = this->feedback_url;
131 
132  cfg["timestamp"] = this->updated;
133  cfg["original_timestamp"] = this->created;
134 }
135 
137 {
138  cfg["version"] = this->version.str();
139  cfg["uploads"] = this->uploads;
140  cfg["type"] = get_addon_type_string(this->type);
141  cfg["title"] = this->title;
142  cfg["dependencies"] = utils::join(this->depends);
143  cfg["core"] = this->core;
144 }
145 
147 {
148  if(this->title.empty()) {
149  return font::escape_text(make_addon_title(this->id));
150  } else {
151  return font::escape_text(this->title);
152  }
153 }
154 
156 
158 {
160 
161  std::string lang_name_short = locale_info.language();
162  std::string lang_name_long = lang_name_short;
163  if(!locale_info.country().empty()) {
164  lang_name_long += '_';
165  lang_name_long += locale_info.country();
166  }
167  if(!locale_info.variant().empty()) {
168  lang_name_long += '@';
169  lang_name_long += locale_info.variant();
170  lang_name_short += '@';
171  lang_name_short += locale_info.variant();
172  }
173 
174  auto info = info_translations.find(lang_name_long);
175  if(info != info_translations.end()) {
176  return info->second;
177  }
178 
179  info = info_translations.find(lang_name_short);
180  if(info != info_translations.end()) {
181  return info->second;
182  }
183 
185 }
186 
188 {
189  addon_info_translation info = this->translated_info();
190 
191  if(info.valid()) {
192  return info.title;
193  }
194 
195  return "";
196 }
197 
199 {
200  std::string title = display_title_translated();
201  return title.empty() ? display_title() : title;
202 }
203 
205 {
206  addon_info_translation info = this->translated_info();
207 
208  if(info.valid() && !info.description.empty()) {
209  return info.description;
210  }
211 
212  return this->description;
213 }
214 
216 {
217  std::string local_title = display_title_translated();
218  if(local_title.empty())
219  return display_title();
220  return local_title + " (" + display_title() + ")";
221 }
222 
224 {
225  std::string ret = icon;
226 
227  if(ret.empty()) {
228  ERR_AC << "add-on '" << id << "' doesn't have an icon path set" << std::endl;
229  } else if(!image::exists(ret)) {
230  ERR_AC << "add-on '" << id << "' has an icon which cannot be found: '" << ret << "'" << std::endl;
231  } else if(ret.find("units/") != std::string::npos && ret.find_first_of('~') == std::string::npos) {
232  // HACK: prevent magenta icons, because they look awful
233  LOG_AC << "add-on '" << id << "' uses a unit baseframe as icon without TC/RC specifications\n";
234  ret += "~RC(magenta>red)";
235  }
236 
237  return ret;
238 }
239 
241 {
242  switch (type) {
243  case ADDON_SP_CAMPAIGN:
244  return _("addon_type^Campaign");
245  case ADDON_SP_SCENARIO:
246  return _("addon_type^Scenario");
248  return _("addon_type^SP/MP Campaign");
249  case ADDON_MP_ERA:
250  return _("addon_type^MP era");
251  case ADDON_MP_FACTION:
252  return _("addon_type^MP faction");
253  case ADDON_MP_MAPS:
254  return _("addon_type^MP map-pack");
255  case ADDON_MP_SCENARIO:
256  return _("addon_type^MP scenario");
257  case ADDON_MP_CAMPAIGN:
258  return _("addon_type^MP campaign");
259  case ADDON_MOD:
260  return _("addon_type^Modification");
261  case ADDON_CORE:
262  return _("addon_type^Core");
263  case ADDON_MEDIA:
264  return _("addon_type^Resources");
265  case ADDON_OTHER:
266  return _("addon_type^Other");
267  default:
268  return _("addon_type^(unknown)");
269  }
270 }
271 
272 std::set<std::string> addon_info::resolve_dependencies(const addons_list& addons) const
273 {
274  std::set<std::string> deps;
275  resolve_deps_recursive(addons, this->id, deps);
276 
277  if(deps.find(this->id) != deps.end()) {
278  LOG_AC << this->id << " depends upon itself; breaking circular dependency\n";
279  deps.erase(this->id);
280  }
281 
282  return deps;
283 }
284 
285 void read_addons_list(const config& cfg, addons_list& dest)
286 {
287  dest.clear();
288 
289  /** @todo FIXME: get rid of this legacy "campaign"/"campaigns" silliness
290  */
291  const config::const_child_itors &addon_cfgs = cfg.child_range("campaign");
292  for(const config& addon_cfg : addon_cfgs) {
293  const std::string& id = addon_cfg["name"].str();
294  if(dest.find(id) != dest.end()) {
295  ERR_AC << "add-ons list has multiple entries for '" << id << "', not good; ignoring them" << std::endl;
296  continue;
297  }
298  dest[id].read(addon_cfg);
299  }
300 }
301 
303 {
304  if(size > 0.0) {
305  return utils::si_string(size, true, _("unit_byte^B"));
306  } else {
307  return "";
308  }
309 }
310 
312 {
313  std::string ret(id);
314  std::replace(ret.begin(), ret.end(), '_', ' ');
315  return ret;
316 }
void read_addons_list(const config &cfg, addons_list &dest)
Definition: info.cpp:285
Modification of the game.
Definition: validation.hpp:52
Single-player scenario.
Definition: validation.hpp:44
void write(config &cfg) const
Definition: info.cpp:108
an add-on that fits in no other category
Definition: validation.hpp:55
std::string display_title_translated() const
Definition: info.cpp:187
Total Conversion Core.
Definition: validation.hpp:42
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
#define ERR_AC
Definition: info.cpp:28
std::string display_title_translated_or_original() const
Definition: info.cpp:198
std::string display_title() const
Get a title or automatic title for display.
Definition: info.cpp:146
logger & info()
Definition: log.cpp:90
static lg::log_domain log_addons_client("addons-client")
child_itors child_range(config_key_type key)
Definition: config.cpp:362
Multiplayer faction.
Definition: validation.hpp:50
std::string str
Definition: statement.cpp:110
std::string title
Definition: info.hpp:35
Definitions for the interface to Wesnoth Markup Language (WML).
void write_minimal(config &cfg) const
Write only minimal WML used for state tracking (_info.cfg) files.
Definition: info.cpp:136
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1028
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:100
#define LOG_AC
Definition: info.cpp:29
static addon_info_translation invalid
Definition: info.hpp:32
std::string si_string(double input, bool base2, const std::string &unit)
Convert into a string with an SI-postfix.
std::string display_icon() const
Get an icon path fixed for display (e.g.
Definition: info.cpp:223
std::string description
Definition: info.hpp:36
std::string display_type() const
Get an add-on type identifier for display in the user&#39;s language.
Definition: info.cpp:240
ADDON_TYPE get_addon_type(const std::string &str)
Definition: validation.cpp:236
Miscellaneous content/media (unit packs, terrain packs, music packs, etc.).
Definition: validation.hpp:54
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:210
void read(const config &cfg)
Definition: info.cpp:76
Multiplayer scenario.
Definition: validation.hpp:47
std::string escape_text(const std::string &text)
Escapes the pango markup characters in a text.
Definition: escape.hpp:32
Multiplayer era.
Definition: validation.hpp:49
u64 size
Definition: statement.cpp:80
std::string size_display_string(double size)
Get a human-readable representation of the specified byte count.
Definition: info.cpp:302
std::string display_title_full() const
Definition: info.cpp:215
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:48
config & add_child(config_key_type key)
Definition: config.cpp:476
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:311
Hybrid campaign.
Definition: validation.hpp:45
void read(const config &cfg)
Definition: info.cpp:62
std::vector< std::string > split(const config_attribute_value &val)
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on&#39;s dependency tree in a recursive fashion.
Definition: info.cpp:272
addon_info_translation translated_info() const
Definition: info.cpp:157
Standard logging facilities (interface).
std::string get_addon_type_string(ADDON_TYPE type)
Definition: validation.cpp:252
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void write(config &cfg) const
Definition: info.cpp:69
std::map< std::string, addon_info > addons_list
Definition: info.hpp:27
Single-player campaign.
Definition: validation.hpp:43
Multiplayer campaign.
Definition: validation.hpp:46
std::string description_translated() const
Definition: info.cpp:204
const boost::locale::info & get_effective_locale_info()
A facet that holds general information about the effective locale.
Definition: gettext.cpp:530