The Battle for Wesnoth  1.15.0-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 
60 void addon_info::read(const config& cfg)
61 {
62  this->id = cfg["name"].str();
63  this->title = cfg["title"].str();
64  this->description = cfg["description"].str();
65  this->icon = cfg["icon"].str();
66  this->version = cfg["version"].str();
67  this->author = cfg["author"].str();
68  this->size = cfg["size"];
69  this->downloads = cfg["downloads"];
70  this->uploads = cfg["uploads"];
71  this->type = get_addon_type(cfg["type"].str());
72 
73  const config::const_child_itors& locales_as_configs = cfg.child_range("translation");
74 
75  for(const config& locale : locales_as_configs) {
76  this->locales.push_back(locale["language"].str());
77  }
78 
79  this->core = cfg["core"].str();
80  this->depends = utils::split(cfg["dependencies"].str());
81  this->tags = utils::split(cfg["tags"].str());
82  this->feedback_url = cfg["feedback_url"].str();
83 
84  this->updated = cfg["timestamp"].to_time_t();
85  this->created = cfg["original_timestamp"].to_time_t();
86 
87  this->local_only = cfg["local_only"].to_bool();
88 }
89 
90 void addon_info::write(config& cfg) const
91 {
92  cfg["id"] = this->id;
93  cfg["title"] = this->title;
94  cfg["description"] = this->description;
95  cfg["icon"] = this->icon;
96  cfg["version"] = this->version.str();
97  cfg["author"] = this->author;
98  cfg["size"] = this->size;
99  cfg["downloads"] = this->downloads;
100  cfg["uploads"] = this->uploads;
101  cfg["type"] = get_addon_type_string(this->type);
102 
103  for (const std::string& locale_id : this->locales) {
104  cfg.add_child("translation")["language"] = locale_id;
105  }
106 
107  cfg["core"] = this->core;
108  cfg["dependencies"] = utils::join(this->depends);
109  cfg["tags"] = utils::join(this->tags);
110  cfg["feedback_url"] = this->feedback_url;
111 
112  cfg["timestamp"] = this->updated;
113  cfg["original_timestamp"] = this->created;
114 }
115 
117 {
118  cfg["version"] = this->version.str();
119  cfg["uploads"] = this->uploads;
120  cfg["type"] = get_addon_type_string(this->type);
121  cfg["title"] = this->title;
122  cfg["dependencies"] = utils::join(this->depends);
123  cfg["core"] = this->core;
124 }
125 
126 std::string addon_info::display_title() const
127 {
128  if(this->title.empty()) {
129  return font::escape_text(make_addon_title(this->id));
130  } else {
131  return font::escape_text(this->title);
132  }
133 }
134 
135 std::string addon_info::display_icon() const
136 {
137  std::string ret = icon;
138 
139  if(ret.empty()) {
140  ERR_AC << "add-on '" << id << "' doesn't have an icon path set" << std::endl;
141  } else if(!image::exists(ret)) {
142  ERR_AC << "add-on '" << id << "' has an icon which cannot be found: '" << ret << "'" << std::endl;
143  } else if(ret.find("units/") != std::string::npos && ret.find_first_of('~') == std::string::npos) {
144  // HACK: prevent magenta icons, because they look awful
145  LOG_AC << "add-on '" << id << "' uses a unit baseframe as icon without TC/RC specifications\n";
146  ret += "~RC(magenta>red)";
147  }
148 
149  return ret;
150 }
151 
152 std::string addon_info::display_type() const
153 {
154  switch (type) {
155  case ADDON_SP_CAMPAIGN:
156  return _("addon_type^Campaign");
157  case ADDON_SP_SCENARIO:
158  return _("addon_type^Scenario");
160  return _("addon_type^SP/MP Campaign");
161  case ADDON_MP_ERA:
162  return _("addon_type^MP era");
163  case ADDON_MP_FACTION:
164  return _("addon_type^MP faction");
165  case ADDON_MP_MAPS:
166  return _("addon_type^MP map-pack");
167  case ADDON_MP_SCENARIO:
168  return _("addon_type^MP scenario");
169  case ADDON_MP_CAMPAIGN:
170  return _("addon_type^MP campaign");
171  case ADDON_MOD:
172  return _("addon_type^Modification");
173  case ADDON_CORE:
174  return _("addon_type^Core");
175  case ADDON_MEDIA:
176  return _("addon_type^Resources");
177  case ADDON_OTHER:
178  return _("addon_type^Other");
179  default:
180  return _("addon_type^(unknown)");
181  }
182 }
183 
184 std::set<std::string> addon_info::resolve_dependencies(const addons_list& addons) const
185 {
186  std::set<std::string> deps;
187  resolve_deps_recursive(addons, this->id, deps);
188 
189  if(deps.find(this->id) != deps.end()) {
190  LOG_AC << this->id << " depends upon itself; breaking circular dependency\n";
191  deps.erase(this->id);
192  }
193 
194  return deps;
195 }
196 
197 void read_addons_list(const config& cfg, addons_list& dest)
198 {
199  dest.clear();
200 
201  unsigned order = 0;
202 
203  /** @todo FIXME: get rid of this legacy "campaign"/"campaigns" silliness
204  */
205  const config::const_child_itors &addon_cfgs = cfg.child_range("campaign");
206  for(const config& addon_cfg : addon_cfgs) {
207  const std::string& id = addon_cfg["name"].str();
208  if(dest.find(id) != dest.end()) {
209  ERR_AC << "add-ons list has multiple entries for '" << id << "', not good; ignoring them" << std::endl;
210  continue;
211  }
212  dest[id].read(addon_cfg);
213  dest[id].order = order++;
214  }
215 }
216 
217 std::string size_display_string(double size)
218 {
219  if(size > 0.0) {
220  return utils::si_string(size, true, _("unit_byte^B"));
221  } else {
222  return "";
223  }
224 }
225 
226 std::string make_addon_title(const std::string& id)
227 {
228  std::string ret(id);
229  std::replace(ret.begin(), ret.end(), '_', ' ');
230  return ret;
231 }
int size
Definition: info.hpp:41
void read_addons_list(const config &cfg, addons_list &dest)
Definition: info.cpp:197
Modification of the game.
Definition: validation.hpp:52
Single-player scenario.
Definition: validation.hpp:44
void write(config &cfg) const
Definition: info.cpp:90
an add-on that fits in no other category
Definition: validation.hpp:55
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:26
std::string display_title() const
Get a title or automatic title for display.
Definition: info.cpp:126
static lg::log_domain log_addons_client("addons-client")
child_itors child_range(config_key_type key)
Definition: config.cpp:366
std::string description
Definition: info.hpp:33
Multiplayer faction.
Definition: validation.hpp:50
std::vector< std::string > tags
Definition: info.hpp:47
std::string feedback_url
Definition: info.hpp:55
Definitions for the interface to Wesnoth Markup Language (WML).
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
void write_minimal(config &cfg) const
Write only minimal WML used for state tracking (_info.cfg) files.
Definition: info.cpp:116
unsigned order
Definition: info.hpp:63
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1217
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
#define LOG_AC
Definition: info.cpp:27
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:135
std::string id
Definition: info.hpp:31
std::string display_type() const
Get an add-on type identifier for display in the user&#39;s language.
Definition: info.cpp:152
ADDON_TYPE type
Definition: info.hpp:45
ADDON_TYPE get_addon_type(const std::string &str)
Definition: validation.cpp:235
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:238
void read(const config &cfg)
Definition: info.cpp:60
bool local_only
Definition: info.hpp:67
std::time_t created
Definition: info.hpp:58
std::vector< std::string > locales
Definition: info.hpp:48
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
std::string icon
Definition: info.hpp:35
int uploads
Definition: info.hpp:43
std::string size_display_string(double size)
Get a human-readable representation of the specified byte count.
Definition: info.cpp:217
std::time_t updated
Definition: info.hpp:57
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:48
config & add_child(config_key_type key)
Definition: config.cpp:479
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:226
std::vector< std::string > depends
Definition: info.hpp:52
Hybrid campaign.
Definition: validation.hpp:45
std::string title
Definition: info.hpp:32
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:184
Standard logging facilities (interface).
std::string str() const
Serializes the version number into string form.
version_info version
Definition: info.hpp:37
std::string get_addon_type_string(ADDON_TYPE type)
Definition: validation.cpp:251
int downloads
Definition: info.hpp:42
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
std::map< std::string, addon_info > addons_list
Definition: info.hpp:26
Single-player campaign.
Definition: validation.hpp:43
Multiplayer campaign.
Definition: validation.hpp:46
std::string author
Definition: info.hpp:39
std::string core
Definition: info.hpp:50