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