The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
addon_utils.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Copyright (C) 2013 - 2015 by Iris Morelle <shadowm2006@gmail.com>
5 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY.
13
14 See the COPYING file for more details.
15*/
16
18
19#include "config.hpp"
20#include "filesystem.hpp"
21#include "log.hpp"
23#include "addon/validation.hpp"
24
25#include <boost/algorithm/string.hpp>
26
27static lg::log_domain log_network("network");
28#define LOG_CS LOG_STREAM_NAMELESS(err, log_network)
29
30namespace {
31
32typedef std::map<std::string, std::string> plain_string_map;
33
34/**
35 * Quick and dirty alternative to @a utils::interpolate_variables_into_string
36 * that doesn't require formula AI code. It is definitely NOT safe for normal
37 * use since it doesn't do strict checks on where variable placeholders
38 * ("$foobar") end and doesn't support pipe ("|") terminators.
39 *
40 * @param str The format string.
41 * @param symbols The symbols table.
42 */
43std::string fast_interpolate_variables_into_string(const std::string &str, const plain_string_map * const symbols)
44{
45 std::string res = str;
46
47 if(symbols) {
48 for(const plain_string_map::value_type& sym : *symbols) {
49 boost::replace_all(res, "$" + sym.first, sym.second);
50 }
51 }
52
53 return res;
54}
55
56} // end anonymous namespace
57
58namespace campaignd {
59
60// Markup characters recognized by GUI1 code. These must be
61// the same as the constants defined in marked-up_text.cpp.
62const std::string illegal_markup_chars = "*`~{^}|@#<&";
63
64std::string format_addon_feedback_url(const std::string& format, const config& params)
65{
66 if(!format.empty() && !params.empty()) {
67 plain_string_map escaped;
68
69 // Percent-encode parameter values for URL interpolation. This is
70 // VERY important since otherwise people could e.g. alter query
71 // strings from the format string.
72 for(const auto& [key, value] : params.attribute_range()) {
73 escaped[key] = utils::urlencode(value.str());
74 }
75
76 // FIXME: We cannot use utils::interpolate_variables_into_string
77 // because it is implemented using a lot of formula AI junk
78 // that really doesn't belong in campaignd.
79 const std::string& res =
80 fast_interpolate_variables_into_string(format, &escaped);
81
82 if(res != format) {
83 return res;
84 }
85
86 // If we get here, that means that no interpolation took place; in
87 // that case, the parameters table probably contains entries that
88 // do not match the format string expectations.
89 }
90
91 return std::string();
92}
93
94void support_translation(config& addon, const std::string& locale_id)
95{
96 config* locale = addon.find_child("translation", "language", locale_id).ptr();
97 if(!locale) {
98 locale = &addon.add_child("translation");
99 (*locale)["language"] = locale_id;
100 }
101 (*locale)["supported"] = true;
102}
103
104void find_translations(const config& base_dir, config& addon)
105{
106 for(const config& file : base_dir.child_range("file")) {
107 const std::string& fn = file["name"].str();
108 if(boost::algorithm::ends_with(fn, ".po")) {
110 }
111 }
112
113 for(const config &dir : base_dir.child_range("dir"))
114 {
115 if(dir["name"] == "LC_MESSAGES") {
116 support_translation(addon, base_dir["name"]);
117 } else {
118 find_translations(dir, addon);
119 }
120 }
121}
122
124{
125 auto dir = cfg.optional_child("dir");
126
127 // No top-level directory? Hm..
128 if(!dir) {
129 LOG_CS << "Could not find toplevel [dir] tag";
130 return;
131 }
132
133 // Don't add if it already exists.
134 if(dir->find_child("file", "name", "COPYING.txt")
135 || dir->find_child("file", "name", "COPYING")
136 || dir->find_child("file", "name", "copying.txt")
137 || dir->find_child("file", "name", "Copying.txt")
138 || dir->find_child("file", "name", "COPYING.TXT"))
139 {
140 return;
141 }
142
143 // Copy over COPYING.txt
144 const std::string& contents = filesystem::read_file("COPYING.txt");
145 if (contents.empty()) {
146 LOG_CS << "Could not find COPYING.txt, path is \"" << game_config::path << "\"";
147 return;
148 }
149
150 config& copying = dir->add_child("file");
151 copying["name"] = "COPYING.txt";
152 copying["contents"] = contents;
153}
154
155std::map<version_info, config> get_version_map(config& addon)
156{
157 std::map<version_info, config> version_map;
158
159 for(config& version : addon.child_range("version")) {
160 version_map.emplace(version_info(version["version"]), version);
161 }
162
163 return version_map;
164}
165
166bool data_apply_removelist(config& data, const config& removelist)
167{
168 for(const config& f : removelist.child_range("file")) {
169 data.remove_children("file", [&f](const config& d) { return f["name"] == d["name"]; });
170 }
171
172 for(const config& dir : removelist.child_range("dir")) {
173 auto data_dir = data.find_child("dir", "name", dir["name"]);
174 if(data_dir && !data_apply_removelist(*data_dir, dir)) {
175 // Delete empty directories
176 data.remove_children("dir", [&dir](const config& d) { return dir["name"] == d["name"]; });
177 }
178 }
179
180 return data.has_child("file") || data.has_child("dir");
181}
182
183void data_apply_addlist(config& data, const config& addlist)
184{
185 for(const config& f : addlist.child_range("file")) {
186 // Just add it since we have already checked the data for duplicates
187 data.add_child("file", f);
188 }
189
190 for(const config& dir : addlist.child_range("dir")) {
191 config* data_dir = data.find_child("dir", "name", dir["name"]).ptr();
192 if(!data_dir) {
193 data_dir = &data.add_child("dir");
194 (*data_dir)["name"] = dir["name"];
195 }
196 data_apply_addlist(*data_dir, dir);
197 }
198}
199
200} // end namespace campaignd
#define LOG_CS
static lg::log_domain log_network("network")
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
const_attr_itors attribute_range() const
Definition config.cpp:740
child_itors child_range(std::string_view key)
Definition config.cpp:268
optional_config_impl< config > find_child(std::string_view key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition config.cpp:764
bool empty() const
Definition config.cpp:823
void remove_children(std::string_view key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
Definition config.cpp:634
Represents version numbers.
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Declarations for File-IO.
Standard logging facilities (interface).
std::string format_addon_feedback_url(const std::string &format, const config &params)
Format a feedback URL for an add-on.
void data_apply_addlist(config &data, const config &addlist)
const std::string illegal_markup_chars
Markup characters recognized by GUI1 code.
void find_translations(const config &base_dir, config &addon)
Scans an add-on archive directory for translations.
bool data_apply_removelist(config &data, const config &removelist)
void support_translation(config &addon, const std::string &locale_id)
std::map< version_info, config > get_version_map(config &addon)
void add_license(config &cfg)
Adds a COPYING.txt file with the full text of the GNU GPL to an add-on.
std::string base_name(const std::string &file, const bool remove_extension=false)
Returns the base filename of a file, with directory name stripped.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string path
std::string urlencode(std::string_view str)
Percent-escape characters in a UTF-8 string intended to be part of a URL.
std::string_view data
Definition picture.cpp:188
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
#define d
#define f