1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2008 Aethyra Development Team
4 * Copyright (C) 2011-2019 The ManaPlus Developers
5 * Copyright (C) 2019-2021 Andrei Karas
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/colordb.h"
24
25 #include "configuration.h"
26
27 #include "utils/cast.h"
28 #include "utils/checkutils.h"
29
30 #include "resources/beingcommon.h"
31
32 #include "debug.h"
33
34 namespace
35 {
36 int mHairColorsSize = 0;
37 bool mLoaded = false;
38 std::string mFail("#ffffff");
39 ColorDB::ColorLists mColorLists;
40 } // namespace
41
load()42 void ColorDB::load()
43 {
44 if (mLoaded)
45 unload();
46
47 logger->log1("Initializing color database...");
48
49 std::map<ItemColor, ItemColorData> colors;
50 ColorListsIterator it = mColorLists.find("hair");
51 if (it != mColorLists.end())
52 colors = it->second;
53 loadHair(paths.getStringValue("hairColorFile"),
54 colors,
55 SkipError_true);
56 loadHair(paths.getStringValue("hairColorPatchFile"),
57 colors,
58 SkipError_true);
59 StringVect list;
60 VirtFs::getFilesInDir(paths.getStringValue(
61 "hairColorPatchDir"), list, ".xml");
62 FOR_EACH (StringVectCIter, it2, list)
63 loadHair(*it2, colors, SkipError_true);
64
65 mColorLists["hair"] = colors;
66
67 loadColorLists(paths.getStringValue("itemColorsFile"),
68 SkipError_false);
69 loadColorLists(paths.getStringValue("itemColorsPatchFile"),
70 SkipError_true);
71 loadXmlDir("itemColorsPatchDir", loadColorLists)
72
73 it = mColorLists.find("hair");
74 if (it != mColorLists.end())
75 mHairColorsSize = CAST_S32((*it).second.size());
76 else
77 mHairColorsSize = 0;
78 mLoaded = true;
79 }
80
loadHair(const std::string & fileName,std::map<ItemColor,ItemColorData> & colors,const SkipError skipError)81 void ColorDB::loadHair(const std::string &fileName,
82 std::map<ItemColor, ItemColorData> &colors,
83 const SkipError skipError)
84 {
85 XML::Document *doc = new XML::Document(fileName,
86 UseVirtFs_true,
87 skipError);
88 XmlNodeConstPtrConst root = doc->rootNode();
89
90 if ((root == nullptr) || !xmlNameEqual(root, "colors"))
91 {
92 logger->log("ColorDB: Failed to find hair colors file.");
93 if (colors.find(ItemColor_zero) == colors.end())
94 colors[ItemColor_zero] = ItemColorData(ItemColor_zero, "", "");
95 delete doc;
96 return;
97 }
98
99 reportAlways("Found legacy hair.xml")
100 for_each_xml_child_node(node, root)
101 {
102 if (xmlNameEqual(node, "include"))
103 {
104 const std::string name = XML::getProperty(node, "name", "");
105 if (!name.empty())
106 loadHair(name, colors, skipError);
107 continue;
108 }
109 else if (xmlNameEqual(node, "color"))
110 {
111 const ItemColor id = fromInt(XML::getProperty(
112 node, "id", 0), ItemColor);
113
114 if (colors.find(id) != colors.end())
115 {
116 reportAlways("ColorDB: Redefinition of dye ID %d",
117 toInt(id, int))
118 }
119
120 colors[id] = ItemColorData(id, XML::langProperty(node, "name", ""),
121 XML::getProperty(node, "value", "#FFFFFF"));
122 }
123 }
124
125 delete doc;
126 }
127
loadColorLists(const std::string & fileName,const SkipError skipError)128 void ColorDB::loadColorLists(const std::string &fileName,
129 const SkipError skipError)
130 {
131 XML::Document *doc = new XML::Document(fileName,
132 UseVirtFs_true,
133 skipError);
134 XmlNodeConstPtrConst root = doc->rootNode();
135 if (root == nullptr)
136 {
137 delete doc;
138 return;
139 }
140
141 for_each_xml_child_node(node, root)
142 {
143 if (xmlNameEqual(node, "include"))
144 {
145 const std::string name = XML::getProperty(node, "name", "");
146 if (!name.empty())
147 loadColorLists(name, skipError);
148 continue;
149 }
150 else if (xmlNameEqual(node, "list"))
151 {
152 const std::string name = XML::getProperty(node, "name", "");
153 if (name.empty())
154 continue;
155
156 std::map <ItemColor, ItemColorData> colors;
157 const ColorListsIterator it = mColorLists.find(name);
158
159 if (it != mColorLists.end())
160 colors = it->second;
161
162 for_each_xml_child_node(colorNode, node)
163 {
164 if (xmlNameEqual(colorNode, "color"))
165 {
166 const int id = XML::getProperty(colorNode, "id", -1);
167 if (id > -1)
168 {
169 ItemColorData c(fromInt(id, ItemColor),
170 XML::langProperty(colorNode, "name", ""),
171 XML::getProperty(colorNode, "value", ""));
172 colors[c.id] = c;
173 }
174 }
175 }
176 mColorLists[name] = colors;
177 }
178 }
179 delete doc;
180 }
181
unload()182 void ColorDB::unload()
183 {
184 logger->log1("Unloading color database...");
185
186 mColorLists.clear();
187 mLoaded = false;
188 }
189
getHairColorName(const ItemColor id)190 std::string &ColorDB::getHairColorName(const ItemColor id)
191 {
192 if (!mLoaded)
193 load();
194
195 const ColorListsIterator it = mColorLists.find("hair");
196 if (it == mColorLists.end())
197 {
198 reportAlways("ColorDB: Error, hair colors list empty")
199 return mFail;
200 }
201
202 const ColorIterator i = (*it).second.find(id);
203
204 if (i == (*it).second.end())
205 {
206 reportAlways("ColorDB: Error, unknown dye ID# %d",
207 toInt(id, int))
208 return mFail;
209 }
210 return i->second.name;
211 }
212
getHairSize()213 int ColorDB::getHairSize()
214 {
215 return mHairColorsSize;
216 }
217
218 const std::map <ItemColor, ItemColorData>
getColorsList(const std::string & name)219 *ColorDB::getColorsList(const std::string &name)
220 {
221 const ColorListsIterator it = mColorLists.find(name);
222
223 if (it != mColorLists.end())
224 return &it->second;
225 return nullptr;
226 }
227