ManaPlus
unitsdb.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "resources/db/unitsdb.h"
25 
26 #include "configuration.h"
27 
29 
30 #include "utils/cast.h"
31 #include "utils/checkutils.h"
32 #include "utils/stdmove.h"
33 
34 #include "resources/beingcommon.h"
35 
36 #include <climits>
37 
38 #include "debug.h"
39 
40 namespace
41 {
43  {
45 
46  std::string symbol;
47  int count;
48  int round;
49  std::string separator;
50  };
51 
53  {
55 
57  double conversion;
58  bool mix;
59  };
60 
63 
65 } // namespace
66 
67 static std::string formatUnit(const int value,
68  const UnitDescription &ud);
69 
70 static std::string splitNumber(std::string str,
71  const std::string &separator);
72 
73 void UnitsDb::load()
74 {
75  logger->log1("Initializing unit database...");
76  { // Setup default weight
77  UnitDescription ud;
78 
79  ud.conversion = 1.0;
80  ud.mix = false;
81 
82  const UnitLevel bu =
83  {
84  "g",
85  1,
86  0,
87  ""
88  };
89  ud.levels.push_back(bu);
90 
91  const UnitLevel ul =
92  {
93  "kg",
94  1000,
95  2,
96  ""
97  };
98  ud.levels.push_back(ul);
99 
100  defaultWeight = ud;
101  }
102 
103  { // Setup default currency
104  UnitDescription ud;
105 
106  ud.conversion = 1.0;
107  ud.mix = false;
108 
109  const UnitLevel bu = {"¤", 1, 0, ""};
110  ud.levels.push_back(bu);
111 
112  defaultCurrency = ud;
113  }
114 
116  loadXmlFile(paths.getStringValue("unitsPatchFile"), SkipError_true);
117  loadXmlDir("unitsPatchDir", loadXmlFile)
118 }
119 
121 {
122  logger->log1("Unloading unit database...");
123  mCurrencies.clear();
124 }
125 
126 static UnitDescription loadUnit(XmlNodePtr node)
127 {
128  UnitDescription ud;
129  int level = 1;
130  ud.conversion = XML::getProperty(node, "conversion", 1);
131  ud.mix = XML::getProperty(node, "mix", "no") == "yes";
132 
133  UnitLevel bu;
134  bu.symbol = XML::getProperty(node, "base", "¤");
135  bu.count = 1;
136  bu.round = XML::getProperty(node, "round", 2);
137  bu.separator = XML::getProperty(node, "separator", " ");
138 
139  ud.levels.push_back(bu);
140 
141  for_each_xml_child_node(uLevel, node)
142  {
143  if (xmlNameEqual(uLevel, "level"))
144  {
145  const UnitLevel ul =
146  {
147  XML::getProperty(uLevel, "symbol",
148  strprintf("¤%d", level)),
149  XML::getProperty(uLevel, "count", -1),
150  XML::getProperty(uLevel, "round", bu.round),
151  XML::getProperty(uLevel, "separator", bu.separator)
152  };
153 
154  if (ul.count > 0)
155  {
156  ud.levels.push_back(ul);
157  level++;
158  }
159  else
160  {
161  logger->log("Error bad unit count: %d for %s in %s",
162  ul.count,
163  ul.symbol.c_str(),
164  bu.symbol.c_str());
165  }
166  }
167  }
168 
169  // Add one more level for saftey
170  const UnitLevel lev =
171  {
172  "",
173  INT_MAX,
174  0,
175  ""
176  };
177  ud.levels.push_back(lev);
178  return ud;
179 }
180 
181 static void loadCurrencies(XmlNodePtr parentNode)
182 {
183  for_each_xml_child_node(node, parentNode)
184  {
185  if (xmlNameEqual(node, "unit"))
186  {
187  const std::string name = XML::getProperty(node, "name", "");
188  if (name.empty())
189  {
190  reportAlways("Error: unknown currency name.")
191  continue;
192  }
193  mCurrencies[name] = loadUnit(node);
194  if (name == DEFAULT_CURRENCY)
196  }
197  }
198 }
199 
200 void UnitsDb::loadXmlFile(const std::string &fileName,
201  const SkipError skipError)
202 {
203  XML::Document doc(fileName, UseVirtFs_true, skipError);
204  XmlNodeConstPtrConst root = doc.rootNode();
205 
206  if ((root == nullptr) || !xmlNameEqual(root, "units"))
207  {
208  logger->log("Error loading unit definition file: "
209  + paths.getStringValue("unitsFile"));
210  return;
211  }
212 
213  for_each_xml_child_node(node, root)
214  {
215  if (xmlNameEqual(node, "include"))
216  {
217  const std::string name = XML::getProperty(node, "name", "");
218  if (!name.empty())
219  loadXmlFile(name, skipError);
220  continue;
221  }
222  else if (xmlNameEqual(node, "unit"))
223  {
224  const std::string type = XML::getProperty(node, "type", "");
225  UnitDescription ud = loadUnit(node);
226  if (type == "weight")
227  {
228  defaultWeight = ud;
229  }
230  else if (type == "currency")
231  {
232  defaultCurrency = ud;
234  }
235  else
236  {
237  logger->log("Error unknown unit type: %s", type.c_str());
238  }
239  }
240  else if (xmlNameEqual(node, "currency"))
241  {
242  loadCurrencies(node);
243  }
244  }
245 }
246 
247 static std::string formatUnit(const int value,
248  const UnitDescription &ud)
249 {
250  UnitLevel ul;
251 
252  // Shortcut for 0; do the same for values less than 0 (for now)
253  if (value <= 0)
254  {
255  ul = ud.levels[0];
256  return strprintf("0%s", ul.symbol.c_str());
257  }
258 
259  double amount = ud.conversion * value;
260  const unsigned int sz = CAST_U32(ud.levels.size());
261 
262  // If only the first level is needed, act like mix if false
263  if (ud.mix && !ud.levels.empty() && ud.levels[1].count < amount)
264  {
265  std::string output;
266  UnitLevel pl = ud.levels[0];
267  ul = ud.levels[1];
268  int levelAmount = CAST_S32(amount);
269  int nextAmount = 0;
270 
271  if (ul.count != 0)
272  levelAmount /= ul.count;
273 
274  amount -= static_cast<double>(levelAmount * ul.count);
275 
276  if (amount > 0)
277  {
278  output = splitNumber(strprintf("%.*f", pl.round,
279  amount), pl.separator).append(pl.symbol);
280  }
281 
282  for (unsigned int i = 2; i < sz; i++)
283  {
284  pl = ul;
285  ul = ud.levels[i];
286 
287  if (ul.count != 0)
288  {
289  nextAmount = levelAmount / ul.count;
290  levelAmount %= ul.count;
291  }
292 
293  if (levelAmount > 0)
294  {
295  output = splitNumber(strprintf("%d", levelAmount),
296  pl.separator).append(pl.symbol).append(output);
297  }
298 
299  if (nextAmount == 0)
300  break;
301  levelAmount = nextAmount;
302  }
303 
304  return output;
305  }
306 
307  ul.round = 0;
308  for (unsigned int i = 0; i < sz; i++)
309  {
310  ul = ud.levels[i];
311  if (amount < ul.count && ul.count > 0)
312  {
313  ul = ud.levels[i - 1];
314  break;
315  }
316  if (ul.count != 0)
317  amount /= ul.count;
318  }
319 
320  return splitNumber(strprintf("%.*f", ul.round, amount),
321  ul.separator).append(ul.symbol);
322 }
323 
324 std::string UnitsDb::formatCurrency(const int value)
325 {
326  return formatUnit(value, defaultCurrency);
327 }
328 
329 std::string UnitsDb::formatCurrency64(const int64_t value)
330 {
331  return formatUnit(CAST_S32(value),
333 }
334 
335 std::string UnitsDb::formatCurrency(std::string name,
336  const int value)
337 {
338  if (mCurrencies.find(name) == mCurrencies.end())
339  name = DEFAULT_CURRENCY;
340  return formatUnit(value, mCurrencies[name]);
341 }
342 
343 std::string UnitsDb::formatCurrency64(std::string name,
344  const int64_t value)
345 {
346  if (mCurrencies.find(name) == mCurrencies.end())
347  name = DEFAULT_CURRENCY;
348  return formatUnit(CAST_S32(value),
349  mCurrencies[name]);
350 }
351 
352 std::string UnitsDb::formatWeight(const int value)
353 {
354  return formatUnit(value, defaultWeight);
355 }
356 
357 bool UnitsDb::existsCurrency(const std::string &name)
358 {
359  return mCurrencies.find(name) != mCurrencies.end();
360 }
361 
362 static std::string splitNumber(std::string str,
363  const std::string &separator)
364 {
365  std::string lastPart;
366  const size_t point = str.find('.');
367  if (point != std::string::npos)
368  {
369  lastPart = str.substr(point);
370  str = str.substr(0, point);
371  }
372  std::string result;
373 
374  if (!str.empty())
375  {
376  size_t sz = str.size();
377  while (sz >= 3)
378  {
379  if (sz >= 6)
380  {
381  result = std::string(separator).append(str.substr(
382  sz - 3)).append(result);
383  }
384  else
385  {
386  result = str.substr(sz - 3).append(result);
387  }
388  str = str.substr(0, str.size() - 3);
389  sz = str.size();
390  }
391  if (!str.empty())
392  {
393  if (!result.empty())
394  result = std::string(str).append(separator).append(result);
395  else
396  result = STD_MOVE(str);
397  }
398  }
399 
400  return result + lastPart;
401 }
static void loadXmlFile(const std::string &file, const std::string &name, BadgesInfos &arr, const SkipError skipError)
Definition: badgesdb.cpp:43
#define loadXmlDir(name, function)
Definition: beingcommon.h:39
#define CAST_S32
Definition: cast.h:30
#define CAST_U32
Definition: cast.h:31
#define reportAlways(...)
Definition: checkutils.h:253
std::string getStringValue(const std::string &key) const
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
xmlNodePtr rootNode()
Definition: libxml.cpp:169
Configuration paths
const std::string DEFAULT_CURRENCY
Definition: currency.h:27
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:161
#define final
Definition: localconsts.h:46
#define A_DEFAULT_COPY(func)
Definition: localconsts.h:41
Logger * logger
Definition: logger.cpp:89
void load()
Definition: avatardb.cpp:46
bool existsCurrency(const std::string &name)
Definition: unitsdb.cpp:357
std::string formatCurrency64(const int64_t value)
Definition: unitsdb.cpp:329
std::string formatCurrency(const int value)
Definition: unitsdb.cpp:324
std::string formatWeight(const int value)
Definition: unitsdb.cpp:352
void loadXmlFile(const std::string &fileName, const SkipError skipError)
Definition: unitsdb.cpp:200
void unload()
Definition: unitsdb.cpp:120
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:174
std::map< std::string, UnitDescription > mCurrencies
Definition: unitsdb.cpp:64
UnitDescription defaultWeight
Definition: unitsdb.cpp:62
UnitDescription defaultCurrency
Definition: unitsdb.cpp:61
const bool SkipError_false
Definition: skiperror.h:30
const bool SkipError_true
Definition: skiperror.h:30
bool SkipError
Definition: skiperror.h:30
#define STD_MOVE(var)
Definition: stdmove.h:28
std::string strprintf(const char *const format,...)
std::string fileName
Definition: testmain.cpp:39
static std::string splitNumber(std::string str, const std::string &separator)
Definition: unitsdb.cpp:362
static UnitDescription loadUnit(xmlNodePtr node)
Definition: unitsdb.cpp:126
static std::string formatUnit(const int value, const UnitDescription &ud)
Definition: unitsdb.cpp:247
static void loadCurrencies(xmlNodePtr parentNode)
Definition: unitsdb.cpp:181
const bool UseVirtFs_true
Definition: usevirtfs.h:30
#define STD_VECTOR
Definition: vector.h:30