GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
|||
28 |
#include "const/resources/currency.h" |
||
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 |
{ |
||
42 |
11595 |
struct UnitLevel final |
|
43 |
{ |
||
44 |
A_DEFAULT_COPY(UnitLevel) |
||
45 |
|||
46 |
std::string symbol; |
||
47 |
int count; |
||
48 |
int round; |
||
49 |
std::string separator; |
||
50 |
}; |
||
51 |
|||
52 |
✗✗✓✗ ✓✗✓✗ ✓✗✓✗ |
1546 |
struct UnitDescription final |
53 |
{ |
||
54 |
A_DEFAULT_COPY(UnitDescription) |
||
55 |
|||
56 |
STD_VECTOR<UnitLevel> levels; |
||
57 |
double conversion; |
||
58 |
bool mix; |
||
59 |
}; |
||
60 |
|||
61 |
1 |
UnitDescription defaultCurrency; |
|
62 |
1 |
UnitDescription defaultWeight; |
|
63 |
|||
64 |
1 |
std::map<std::string, UnitDescription> mCurrencies; |
|
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 |
77 |
void UnitsDb::load() |
|
74 |
{ |
||
75 |
77 |
logger->log1("Initializing unit database..."); |
|
76 |
{ // Setup default weight |
||
77 |
154 |
UnitDescription ud; |
|
78 |
|||
79 |
77 |
ud.conversion = 1.0; |
|
80 |
77 |
ud.mix = false; |
|
81 |
|||
82 |
const UnitLevel bu = |
||
83 |
{ |
||
84 |
"g", |
||
85 |
1, |
||
86 |
0, |
||
87 |
"" |
||
88 |
✓✗✓✗ |
462 |
}; |
89 |
✓✗ | 77 |
ud.levels.push_back(bu); |
90 |
|||
91 |
const UnitLevel ul = |
||
92 |
{ |
||
93 |
"kg", |
||
94 |
1000, |
||
95 |
2, |
||
96 |
"" |
||
97 |
✓✗✓✗ |
462 |
}; |
98 |
✓✗ | 77 |
ud.levels.push_back(ul); |
99 |
|||
100 |
77 |
defaultWeight = ud; |
|
101 |
} |
||
102 |
|||
103 |
{ // Setup default currency |
||
104 |
154 |
UnitDescription ud; |
|
105 |
|||
106 |
77 |
ud.conversion = 1.0; |
|
107 |
77 |
ud.mix = false; |
|
108 |
|||
109 |
✓✗✓✗ |
462 |
const UnitLevel bu = {"¤", 1, 0, ""}; |
110 |
✓✗ | 77 |
ud.levels.push_back(bu); |
111 |
|||
112 |
77 |
defaultCurrency = ud; |
|
113 |
} |
||
114 |
|||
115 |
✓✗✓✗ |
385 |
loadXmlFile(paths.getStringValue("unitsFile"), SkipError_false); |
116 |
✓✗✓✗ |
385 |
loadXmlFile(paths.getStringValue("unitsPatchFile"), SkipError_true); |
117 |
✓✗✓✗ ✓✗✓✗ ✗✓✗✗ |
1078 |
loadXmlDir("unitsPatchDir", loadXmlFile) |
118 |
77 |
} |
|
119 |
|||
120 |
292 |
void UnitsDb::unload() |
|
121 |
{ |
||
122 |
292 |
logger->log1("Unloading unit database..."); |
|
123 |
292 |
mCurrencies.clear(); |
|
124 |
292 |
} |
|
125 |
|||
126 |
154 |
static UnitDescription loadUnit(XmlNodePtr node) |
|
127 |
{ |
||
128 |
154 |
UnitDescription ud; |
|
129 |
154 |
int level = 1; |
|
130 |
✓✗ | 154 |
ud.conversion = XML::getProperty(node, "conversion", 1); |
131 |
✓✗✓✗ |
770 |
ud.mix = XML::getProperty(node, "mix", "no") == "yes"; |
132 |
|||
133 |
308 |
UnitLevel bu; |
|
134 |
✓✗✓✗ |
770 |
bu.symbol = XML::getProperty(node, "base", "¤"); |
135 |
154 |
bu.count = 1; |
|
136 |
✓✗ | 154 |
bu.round = XML::getProperty(node, "round", 2); |
137 |
✓✗✓✗ |
770 |
bu.separator = XML::getProperty(node, "separator", " "); |
138 |
|||
139 |
✓✗ | 154 |
ud.levels.push_back(bu); |
140 |
|||
141 |
✓✓ | 385 |
for_each_xml_child_node(uLevel, node) |
142 |
{ |
||
143 |
✓✗✓✓ |
231 |
if (xmlNameEqual(uLevel, "level")) |
144 |
{ |
||
145 |
const UnitLevel ul = |
||
146 |
{ |
||
147 |
XML::getProperty(uLevel, "symbol", |
||
148 |
✓✗ | 154 |
strprintf("¤%d", level)), |
149 |
✓✗ | 77 |
XML::getProperty(uLevel, "count", -1), |
150 |
✓✗ | 77 |
XML::getProperty(uLevel, "round", bu.round), |
151 |
XML::getProperty(uLevel, "separator", bu.separator) |
||
152 |
✓✗✓✗ |
308 |
}; |
153 |
|||
154 |
✓✗ | 77 |
if (ul.count > 0) |
155 |
{ |
||
156 |
✓✗ | 77 |
ud.levels.push_back(ul); |
157 |
77 |
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 |
✓✗✓✗ |
924 |
}; |
177 |
✓✗ | 154 |
ud.levels.push_back(lev); |
178 |
154 |
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) |
||
195 |
defaultCurrency = mCurrencies[name]; |
||
196 |
} |
||
197 |
} |
||
198 |
} |
||
199 |
|||
200 |
154 |
void UnitsDb::loadXmlFile(const std::string &fileName, |
|
201 |
const SkipError skipError) |
||
202 |
{ |
||
203 |
231 |
XML::Document doc(fileName, UseVirtFs_true, skipError); |
|
204 |
✓✗ | 154 |
XmlNodeConstPtrConst root = doc.rootNode(); |
205 |
|||
206 |
✓✓✓✗ ✗✓✓✓ |
154 |
if ((root == nullptr) || !xmlNameEqual(root, "units")) |
207 |
{ |
||
208 |
77 |
logger->log("Error loading unit definition file: " |
|
209 |
✓✗✓✗ ✓✗✓✗ |
539 |
+ paths.getStringValue("unitsFile")); |
210 |
77 |
return; |
|
211 |
} |
||
212 |
|||
213 |
✓✓ | 462 |
for_each_xml_child_node(node, root) |
214 |
{ |
||
215 |
✓✗✗✓ |
385 |
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 |
✓✗✓✓ |
385 |
else if (xmlNameEqual(node, "unit")) |
223 |
{ |
||
224 |
✓✗✓✗ |
770 |
const std::string type = XML::getProperty(node, "type", ""); |
225 |
✓✗ | 308 |
UnitDescription ud = loadUnit(node); |
226 |
✓✓ | 154 |
if (type == "weight") |
227 |
{ |
||
228 |
defaultWeight = ud; |
||
229 |
} |
||
230 |
✓✗ | 77 |
else if (type == "currency") |
231 |
{ |
||
232 |
77 |
defaultCurrency = ud; |
|
233 |
✓✗ | 77 |
mCurrencies[DEFAULT_CURRENCY] = ud; |
234 |
} |
||
235 |
else |
||
236 |
{ |
||
237 |
logger->log("Error unknown unit type: %s", type.c_str()); |
||
238 |
} |
||
239 |
} |
||
240 |
✓✗✗✓ |
231 |
else if (xmlNameEqual(node, "currency")) |
241 |
{ |
||
242 |
loadCurrencies(node); |
||
243 |
} |
||
244 |
} |
||
245 |
} |
||
246 |
|||
247 |
3 |
static std::string formatUnit(const int value, |
|
248 |
const UnitDescription &ud) |
||
249 |
{ |
||
250 |
6 |
UnitLevel ul; |
|
251 |
|||
252 |
// Shortcut for 0; do the same for values less than 0 (for now) |
||
253 |
✓✗ | 3 |
if (value <= 0) |
254 |
{ |
||
255 |
✓✗ | 3 |
ul = ud.levels[0]; |
256 |
✓✗ | 3 |
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 |
3 |
std::string UnitsDb::formatCurrency(const int value) |
|
325 |
{ |
||
326 |
3 |
return formatUnit(value, defaultCurrency); |
|
327 |
} |
||
328 |
|||
329 |
std::string UnitsDb::formatCurrency64(const int64_t value) |
||
330 |
{ |
||
331 |
return formatUnit(CAST_S32(value), |
||
332 |
defaultCurrency); |
||
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 |
✓✗✓✗ |
3 |
} |
Generated by: GCOVR (Version 3.3) |