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