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