1 // Copyright (C) 2008, 2009, 2010, 2011, 2014, 2015, 2020 Ben Asselstine
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 // 02110-1301, USA.
17
18 #include <iostream>
19 #include "rectangle.h"
20 #include <sigc++/functors/mem_fun.h>
21
22 #include <string.h>
23 #include "shieldset.h"
24 #include "shieldstyle.h"
25 #include "File.h"
26 #include "Configuration.h"
27 #include "tarhelper.h"
28 #include "file-compat.h"
29 #include "ucompose.hpp"
30 #include "xmlhelper.h"
31
32 Glib::ustring Shieldset::d_tag = "shieldset";
33 Glib::ustring Shieldset::file_extension = SHIELDSET_EXT;
34
35 #define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
36 //#define debug(x)
37
Shieldset(guint32 id,Glib::ustring name)38 Shieldset::Shieldset(guint32 id, Glib::ustring name)
39 : Set(SHIELDSET_EXT, id, name, 0), d_small_height(0), d_small_width(0),
40 d_medium_height(0), d_medium_width(0), d_large_height(0), d_large_width(0)
41 {
42 }
43
Shieldset(const Shieldset & s)44 Shieldset::Shieldset(const Shieldset& s)
45 : std::list<Shield*>(), sigc::trackable(s), Set(s),
46 d_small_height(s.d_small_height), d_small_width(s.d_small_width),
47 d_medium_height(s.d_medium_height), d_medium_width(s.d_medium_width),
48 d_large_height(s.d_large_height), d_large_width(s.d_large_width)
49 {
50 for (const_iterator it = s.begin(); it != s.end(); it++)
51 push_back(new Shield(*(*it)));
52 }
53
Shieldset(XML_Helper * helper,Glib::ustring directory)54 Shieldset::Shieldset(XML_Helper *helper, Glib::ustring directory)
55 : Set(SHIELDSET_EXT, helper)
56 {
57 setDirectory(directory);
58 setTileSize(0);
59 helper->getData(d_small_width, "small_width");
60 helper->getData(d_small_height, "small_height");
61 helper->getData(d_medium_width, "medium_width");
62 helper->getData(d_medium_height, "medium_height");
63 helper->getData(d_large_width, "large_width");
64 helper->getData(d_large_height, "large_height");
65 helper->registerTag(Shield::d_tag,
66 sigc::mem_fun((*this), &Shieldset::loadShield));
67 helper->registerTag(ShieldStyle::d_tag, sigc::mem_fun((*this),
68 &Shieldset::loadShield));
69 helper->registerTag(Tartan::d_tag, sigc::mem_fun((*this),
70 &Shieldset::loadShield));
71 clear();
72 }
73
~Shieldset()74 Shieldset::~Shieldset()
75 {
76 uninstantiateImages();
77 for (iterator it = begin(); it != end(); it++)
78 delete *it;
79 clean_tmp_dir();
80 }
81
lookupShieldByTypeAndColour(guint32 type,guint32 colour) const82 ShieldStyle * Shieldset::lookupShieldByTypeAndColour(guint32 type, guint32 colour) const
83 {
84 for (const_iterator it = begin(); it != end(); it++)
85 {
86 for (Shield::const_iterator i = (*it)->begin(); i != (*it)->end(); i++)
87 {
88 if ((*i)->getType() == type && (*it)->getOwner() == colour)
89 return *i;
90 }
91 }
92 return NULL;
93 }
94
lookupShieldByColour(guint32 colour) const95 Shield * Shieldset::lookupShieldByColour (guint32 colour) const
96 {
97 for (const_iterator it = begin(); it != end(); it++)
98 {
99 if ((*it)->getOwner() == colour)
100 return *it;
101 }
102 return NULL;
103 }
104
getColor(guint32 owner) const105 Gdk::RGBA Shieldset::getColor(guint32 owner) const
106 {
107 for (const_iterator it = begin(); it != end(); it++)
108 {
109 if ((*it)->getOwner() == owner)
110 return (*it)->getColor();
111 }
112 return Gdk::RGBA("black");
113 }
114
loadShield(Glib::ustring tag,XML_Helper * helper)115 bool Shieldset::loadShield(Glib::ustring tag, XML_Helper* helper)
116 {
117 if (tag == Shield::d_tag)
118 {
119 Shield* sh = new Shield(helper);
120 push_back(sh);
121 return true;
122 }
123 if (tag == ShieldStyle::d_tag)
124 {
125 ShieldStyle *sh = new ShieldStyle(helper);
126 (*back()).push_back(sh);
127 return true;
128 }
129 if (tag == Tartan::d_tag)
130 {
131 Tartan * t = new Tartan(helper);
132 back()->setTartanImageName(Tartan::LEFT,
133 t->getTartanImageName(Tartan::LEFT));
134 back()->setTartanImageName(Tartan::CENTER,
135 t->getTartanImageName(Tartan::CENTER));
136 back()->setTartanImageName(Tartan::RIGHT,
137 t->getTartanImageName(Tartan::RIGHT));
138 delete t;
139 return true;
140 }
141 return false;
142 }
143
144 //! Helper class for making a new Shieldset object from a shieldset file.
145 class ShieldsetLoader
146 {
147 public:
ShieldsetLoader(Glib::ustring filename,bool & broken,bool & unsupported)148 ShieldsetLoader(Glib::ustring filename, bool &broken, bool &unsupported)
149 {
150 unsupported_version = false;
151 shieldset = NULL;
152 dir = File::get_dirname(filename);
153 file = File::get_basename(filename);
154 if (File::nameEndsWith(filename, Shieldset::file_extension) == false)
155 filename += Shieldset::file_extension;
156 Tar_Helper t(filename, std::ios::in, broken);
157 if (broken)
158 return;
159 Glib::ustring lwsfilename =
160 t.getFirstFile(Shieldset::file_extension, broken);
161 if (broken)
162 return;
163 XML_Helper helper(lwsfilename, std::ios::in);
164 helper.registerTag(Shieldset::d_tag, sigc::mem_fun((*this), &ShieldsetLoader::load));
165 if (!helper.parseXML())
166 {
167 unsupported = unsupported_version;
168 std::cerr << String::ucompose(_("Error! can't load Shield Set `%1'."), filename) << std::endl;
169 if (shieldset != NULL)
170 delete shieldset;
171 shieldset = NULL;
172 }
173 helper.close();
174 File::erase(lwsfilename);
175 t.Close();
176 };
load(Glib::ustring tag,XML_Helper * helper)177 bool load(Glib::ustring tag, XML_Helper* helper)
178 {
179 if (tag == Shieldset::d_tag)
180 {
181 if (helper->getVersion() == LORDSAWAR_SHIELDSET_VERSION)
182 {
183 shieldset = new Shieldset(helper, dir);
184 shieldset->setBaseName(file);
185 return true;
186 }
187 else
188 {
189 unsupported_version = true;
190 return false;
191 }
192 }
193 return false;
194 };
195 Glib::ustring dir;
196 Glib::ustring file;
197 Shieldset *shieldset;
198 bool unsupported_version;
199 };
200
create(Glib::ustring filename,bool & unsupported_version)201 Shieldset *Shieldset::create(Glib::ustring filename, bool &unsupported_version)
202 {
203 bool broken = false;
204 ShieldsetLoader d(filename, broken, unsupported_version);
205 if (broken)
206 return NULL;
207 return d.shieldset;
208 }
209
save(Glib::ustring filename,Glib::ustring ext) const210 bool Shieldset::save(Glib::ustring filename, Glib::ustring ext) const
211 {
212 bool broken = false;
213 Glib::ustring goodfilename = File::add_ext_if_necessary(filename, ext);
214 Glib::ustring tmpfile = File::get_tmp_file();
215 XML_Helper helper(tmpfile, std::ios::out);
216 helper.begin(LORDSAWAR_SHIELDSET_VERSION);
217 broken = !save(&helper);
218 helper.close();
219 if (broken == true)
220 return false;
221 return saveTar(tmpfile, tmpfile + ".tar", goodfilename);
222 }
223
save(XML_Helper * helper) const224 bool Shieldset::save(XML_Helper *helper) const
225 {
226 bool retval = true;
227
228 retval &= helper->openTag(d_tag);
229 retval &= Set::save(helper);
230 retval &= helper->saveData("small_width", d_small_width);
231 retval &= helper->saveData("small_height", d_small_height);
232 retval &= helper->saveData("medium_width", d_medium_width);
233 retval &= helper->saveData("medium_height", d_medium_height);
234 retval &= helper->saveData("large_width", d_large_width);
235 retval &= helper->saveData("large_height", d_large_height);
236 for (const_iterator it = begin(); it != end(); it++)
237 retval &= (*it)->save(helper);
238 retval &= helper->closeTag();
239 return retval;
240 }
241
instantiateImages(bool scale,bool & broken)242 void Shieldset::instantiateImages(bool scale, bool &broken)
243 {
244 uninstantiateImages();
245 for (iterator it = begin(); it != end(); it++)
246 {
247 (*it)->instantiateImages(this, scale, broken);
248 if (broken)
249 break;
250 }
251 }
252
uninstantiateImages()253 void Shieldset::uninstantiateImages()
254 {
255 for (iterator it = begin(); it != end(); it++)
256 (*it)->uninstantiateImages();
257 }
258
validate() const259 bool Shieldset::validate() const
260 {
261 bool valid = true;
262 if (String::utrim (getName ()) == "")
263 return false;
264 if (validateNumberOfShields() == false)
265 return false;
266 for (unsigned int i = Shield::WHITE; i <= Shield::NEUTRAL; i++)
267 {
268 if (validateShieldImages(Shield::Colour(i)) == false)
269 return false;
270 }
271 for (unsigned int i = Shield::WHITE; i <= Shield::NEUTRAL; i++)
272 {
273 if (validateTartanImages(Shield::Colour(i)) == false)
274 return false;
275 }
276 if (d_small_width == 0 || d_small_height == 0)
277 return false;
278 if (d_medium_width == 0 || d_medium_height == 0)
279 return false;
280 if (d_large_width == 0 || d_large_height == 0)
281 return false;
282 return valid;
283 }
284
validateNumberOfShields() const285 bool Shieldset::validateNumberOfShields() const
286 {
287 int players[MAX_PLAYERS + 1][3];
288 memset(players, 0, sizeof(players));
289 //need at least 3 complete player shields, one of which must be neutral.
290 for (const_iterator it = begin(); it != end(); it++)
291 {
292 for (Shield::const_iterator i = (*it)->begin(); i != (*it)->end(); i++)
293 {
294 int idx = 0;
295 switch ((*i)->getType())
296 {
297 case ShieldStyle::SMALL: idx = 0; break;
298 case ShieldStyle::MEDIUM: idx = 1; break;
299 case ShieldStyle::LARGE: idx = 2; break;
300 }
301 players[(*it)->getOwner()][idx]++;
302 }
303 }
304 int count = 0;
305 for (unsigned int i = 0; i < MAX_PLAYERS + 1; i++)
306 {
307 if (players[i][0] > 0 && players[i][1] > 0 && players[i][2] > 0)
308 count++;
309 }
310 if (count <= 2)
311 return false;
312 if (players[MAX_PLAYERS][0] == 0 || players[MAX_PLAYERS][1] == 0 || players[MAX_PLAYERS][2] == 0)
313 return false;
314 return true;
315 }
316
validateShieldImages(Shield::Colour c) const317 bool Shieldset::validateShieldImages(Shield::Colour c) const
318 {
319 //if we have a shield, it should have all 3 sizes.
320 int player[3];
321 memset(player, 0, sizeof(player));
322 for (const_iterator it = begin(); it != end(); it++)
323 {
324 if ((*it)->getOwner() != guint32(c))
325 continue;
326 for (Shield::const_iterator i = (*it)->begin(); i != (*it)->end(); i++)
327 {
328 int idx = 0;
329 switch ((*i)->getType())
330 {
331 case ShieldStyle::SMALL: idx = 0; break;
332 case ShieldStyle::MEDIUM: idx = 1; break;
333 case ShieldStyle::LARGE: idx = 2; break;
334 }
335 if ((*i)->getImageName().empty() == false)
336 player[idx]++;
337 }
338 }
339 int count = player[0] + player[1] + player[2];
340 if (count <= 2)
341 return false;
342 return true;
343 }
344
validateTartanImages(Shield::Colour c) const345 bool Shieldset::validateTartanImages(Shield::Colour c) const
346 {
347 //if we have a shield, it should have all 3 portions of a tartan.
348 int player[3];
349 memset(player, 0, sizeof(player));
350 for (const_iterator it = begin(); it != end(); it++)
351 {
352 if ((*it)->getOwner() != guint32(c))
353 continue;
354 if ((*it)->getTartanImageName (Tartan::LEFT).empty () == false)
355 player[0]++;
356 if ((*it)->getTartanImageName (Tartan::CENTER).empty () == false)
357 player[1]++;
358 if ((*it)->getTartanImageName (Tartan::RIGHT).empty () == false)
359 player[2]++;
360 }
361 int count = player[0] + player[1] + player[2];
362 if (count <= 2)
363 return false;
364 return true;
365 }
366
reload(bool & broken)367 void Shieldset::reload(bool &broken)
368 {
369 broken = false;
370 bool unsupported_version = false;
371 ShieldsetLoader d(getConfigurationFile(), broken, unsupported_version);
372 if (broken == false && d.shieldset && d.shieldset->validate())
373 {
374 //steal the values from d.shieldset and then don't delete it.
375 uninstantiateImages();
376 for (iterator it = begin(); it != end(); it++)
377 delete *it;
378 Glib::ustring basename = getBaseName();
379 *this = *d.shieldset;
380 instantiateImages(true, broken);
381 setBaseName(basename);
382 }
383 }
384
countEmptyImageNames() const385 guint32 Shieldset::countEmptyImageNames() const
386 {
387 guint32 count = 0;
388 for (Shieldset::const_iterator i = begin(); i != end(); i++)
389 {
390 for (std::list<ShieldStyle*>::const_iterator j = (*i)->begin(); j != (*i)->end(); j++)
391 {
392 if ((*j)->getImageName().empty() == true)
393 count++;
394 }
395 }
396 return count;
397 }
398
upgrade(Glib::ustring filename,Glib::ustring old_version,Glib::ustring new_version)399 bool Shieldset::upgrade(Glib::ustring filename, Glib::ustring old_version, Glib::ustring new_version)
400 {
401 return FileCompat::getInstance()->upgrade(filename, old_version, new_version,
402 FileCompat::SHIELDSET, d_tag);
403 }
404
support_backward_compatibility()405 void Shieldset::support_backward_compatibility()
406 {
407 FileCompat::getInstance()->support_type(FileCompat::SHIELDSET,
408 file_extension, d_tag, true);
409 FileCompat::getInstance()->support_version
410 (FileCompat::SHIELDSET, "0.2.1", "0.3.2",
411 sigc::ptr_fun(&Shieldset::upgrade));
412 }
413
copy(const Shieldset * shieldset)414 Shieldset* Shieldset::copy(const Shieldset *shieldset)
415 {
416 if (!shieldset)
417 return NULL;
418 return new Shieldset(*shieldset);
419 }
420
setHeightsAndWidthsFromImages()421 void Shieldset::setHeightsAndWidthsFromImages()
422 {
423 setSmallHeightsAndWidthsFromImages();
424 setMediumHeightsAndWidthsFromImages();
425 setLargeHeightsAndWidthsFromImages();
426 }
427
setHeightsAndWidthsFromImages(ShieldStyle * ss)428 void Shieldset::setHeightsAndWidthsFromImages(ShieldStyle *ss)
429 {
430 ShieldStyle::Type t = ShieldStyle::Type(ss->getType ());
431 switch (t)
432 {
433 case ShieldStyle::SMALL:
434 return setSmallHeightsAndWidthsFromImages();
435 case ShieldStyle::MEDIUM:
436 return setMediumHeightsAndWidthsFromImages();
437 case ShieldStyle::LARGE:
438 return setLargeHeightsAndWidthsFromImages();
439 }
440 }
441
setSmallHeightsAndWidthsFromImages()442 void Shieldset::setSmallHeightsAndWidthsFromImages()
443 {
444 d_small_width = 0;
445 d_small_height = 0;
446 std::map<Vector<int>, guint32> small_sizecounts;
447
448 for (iterator it = begin(); it != end(); it++)
449 for (Shield::iterator i = (*it)->begin(); i != (*it)->end(); i++)
450 {
451 PixMask *image = (*i)->getImage();
452 if (image == NULL)
453 continue;
454 switch ((*i)->getType ())
455 {
456 case ShieldStyle::SMALL:
457 small_sizecounts[image->get_unscaled_dim ()]++;
458 break;
459 }
460 }
461
462 guint32 maxcount = 0;
463 for (auto i : small_sizecounts)
464 {
465 if (i.second > maxcount)
466 {
467 maxcount = i.second;
468 d_small_width = i.first.x;
469 d_small_height = i.first.y;
470 }
471 }
472 return;
473 }
474
setMediumHeightsAndWidthsFromImages()475 void Shieldset::setMediumHeightsAndWidthsFromImages()
476 {
477 d_medium_width = 0;
478 d_medium_height = 0;
479 std::map<Vector<int>, guint32> medium_sizecounts;
480
481 for (iterator it = begin(); it != end(); it++)
482 for (Shield::iterator i = (*it)->begin(); i != (*it)->end(); i++)
483 {
484 PixMask *image = (*i)->getImage();
485 if (image == NULL)
486 continue;
487 switch ((*i)->getType ())
488 {
489 case ShieldStyle::MEDIUM:
490 medium_sizecounts[image->get_unscaled_dim ()]++;
491 break;
492 }
493 }
494
495 guint32 maxcount = 0;
496 for (auto i : medium_sizecounts)
497 {
498 if (i.second > maxcount)
499 {
500 maxcount = i.second;
501 d_medium_width = i.first.x;
502 d_medium_height = i.first.y;
503 }
504 }
505 return;
506 }
507
setLargeHeightsAndWidthsFromImages()508 void Shieldset::setLargeHeightsAndWidthsFromImages()
509 {
510 d_large_width = 0;
511 d_large_height = 0;
512 std::map<Vector<int>, guint32> large_sizecounts;
513
514 for (iterator it = begin(); it != end(); it++)
515 for (Shield::iterator i = (*it)->begin(); i != (*it)->end(); i++)
516 {
517 PixMask *image = (*i)->getImage();
518 if (image == NULL)
519 continue;
520 switch ((*i)->getType ())
521 {
522 case ShieldStyle::LARGE:
523 large_sizecounts[image->get_unscaled_dim ()]++;
524 break;
525 }
526 }
527
528 guint32 maxcount = 0;
529 for (auto i : large_sizecounts)
530 {
531 if (i.second > maxcount)
532 {
533 maxcount = i.second;
534 d_large_width = i.first.x;
535 d_large_height = i.first.y;
536 }
537 }
538 return;
539 }
540
lookupTartanImage(guint32 colour,Tartan::Type type,PixMask ** image,PixMask ** mask)541 void Shieldset::lookupTartanImage(guint32 colour, Tartan::Type type,
542 PixMask **image, PixMask **mask)
543 {
544 for (const_iterator it = begin(); it != end(); it++)
545 {
546 for (Shield::const_iterator i = (*it)->begin(); i != (*it)->end(); i++)
547 {
548 if ((*it)->getOwner() == colour)
549 {
550 switch (type)
551 {
552 case Tartan::LEFT:
553 *image = (*it)->getImage(Tartan::LEFT);
554 *mask = (*it)->getMask(Tartan::LEFT);
555 break;
556 case Tartan::CENTER:
557 *image = (*it)->getImage(Tartan::CENTER);
558 *mask = (*it)->getMask(Tartan::CENTER);
559 break;
560 case Tartan::RIGHT:
561 *image = (*it)->getImage(Tartan::RIGHT);
562 *mask = (*it)->getMask(Tartan::RIGHT);
563 break;
564 }
565 break;
566 }
567 }
568 }
569 return;
570 }
571
uninstantiateSameNamedImages(Glib::ustring name)572 void Shieldset ::uninstantiateSameNamedImages (Glib::ustring name)
573 {
574 for (auto s : *this)
575 {
576 for (auto ss : *s)
577 if (ss->getImageName () == name)
578 {
579 ss->uninstantiateImages ();
580 ss->setImageName ("");
581 }
582 if (s->getTartanImageName (Tartan::LEFT) == name)
583 {
584 s->uninstantiateTartanImage (Tartan::LEFT);
585 s->setTartanImageName (Tartan::LEFT, "");
586 }
587 if (s->getTartanImageName (Tartan::CENTER) == name)
588 {
589 s->uninstantiateTartanImage (Tartan::CENTER);
590 s->setTartanImageName (Tartan::CENTER, "");
591 }
592 if (s->getTartanImageName (Tartan::RIGHT) == name)
593 {
594 s->uninstantiateTartanImage (Tartan::RIGHT);
595 s->setTartanImageName (Tartan::RIGHT, "");
596 }
597 }
598 }
599
isAnyHeightAndWidthSet()600 bool Shieldset::isAnyHeightAndWidthSet()
601 {
602 return
603 isSmallHeightAndWidthSet () ||
604 isMediumHeightAndWidthSet () ||
605 isLargeHeightAndWidthSet ();
606 }
607
isSmallHeightAndWidthSet()608 bool Shieldset::isSmallHeightAndWidthSet()
609 {
610 return d_small_width && d_small_height;
611 }
612
isMediumHeightAndWidthSet()613 bool Shieldset::isMediumHeightAndWidthSet()
614 {
615 return d_medium_width && d_medium_height;
616 }
617
isLargeHeightAndWidthSet()618 bool Shieldset::isLargeHeightAndWidthSet()
619 {
620 return d_large_width && d_large_height;
621 }
622 //End of file
623