initial commit, lordsawar source, slightly modified
[lordsawar] / src / armysetlist.cpp
1 // Copyright (C) 2001, 2002, 2003 Michael Bartl
2 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2004, 2005 Andrea Paternesi
4 // Copyright (C) 2007, 2008, 2009 Ben Asselstine
5 //
6 //  This program is free software; you can redistribute it and/or modify
7 //  it under the terms of the GNU General Public License as published by
8 //  the Free Software Foundation; either version 3 of the License, or
9 //  (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU Library General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
19 //  02110-1301, USA.
20
21 #include <iostream>
22 #include <algorithm>
23 #include <expat.h>
24 #include <gtkmm.h>
25 #include <sigc++/functors/mem_fun.h>
26 #include <assert.h>
27
28 #include "rectangle.h"
29 #include "armysetlist.h"
30 #include "armyset.h"
31 #include "File.h"
32 #include "defs.h"
33 #include "ucompose.hpp"
34 #include "PixMask.h"
35 #include "tarhelper.h"
36
37
38 using namespace std;
39
40 #define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
41 //#define debug(x)
42
43 Armysetlist* Armysetlist::s_instance = 0;
44
45 Armysetlist* Armysetlist::getInstance()
46 {
47   if (!s_instance)
48     s_instance = new Armysetlist();
49
50   return s_instance;
51 }
52
53 void Armysetlist::deleteInstance()
54 {
55   if (s_instance)
56     delete s_instance;
57
58   s_instance = 0;
59 }
60
61 void Armysetlist::add(Armyset *armyset)
62 {
63   std::string subdir = File::get_basename(armyset->getDirectory());
64   push_back(armyset); 
65   for (Armyset::iterator ait = armyset->begin(); ait != armyset->end(); ait++)
66     d_armies[armyset->getId()].push_back(*ait);
67   d_names[armyset->getId()] = armyset->getName();
68   d_ids[String::ucompose("%1 %2", armyset->getName(), armyset->getTileSize())] = armyset->getId();
69   armyset->setSubDir(subdir);
70   d_armysets[subdir] = armyset;
71   d_armysetids[armyset->getId()] = armyset;
72 }
73
74 void Armysetlist::loadArmysets(std::list<std::string> armysets)
75 {
76   for (std::list<std::string>::const_iterator i = armysets.begin(); 
77        i != armysets.end(); i++)
78     {
79       Armyset *armyset = loadArmyset(*i);
80       if (!armyset)
81         continue;
82
83       add(armyset);
84     }
85 }
86
87 Armysetlist::Armysetlist()
88 {
89   // load all armysets
90   std::list<std::string> armysets = Armyset::scanSystemCollection();
91   loadArmysets(armysets);
92   armysets = Armyset::scanUserCollection();
93   loadArmysets(armysets);
94
95 }
96
97 Armysetlist::~Armysetlist()
98 {
99   for (iterator it = begin(); it != end(); it++)
100     delete (*it);
101
102   // remove all army entries
103   for (ArmyPrototypeMap::iterator it = d_armies.begin(); 
104        it != d_armies.end(); it++)
105     while (!(*it).second.empty())
106       delete ((*it).second)[0];
107 }
108
109 ArmyProto* Armysetlist::getArmy(guint32 id, guint32 index) const
110 {
111   // always use ArmyProtoMap::find for searching, else a default entry is 
112   // created, which can produce really bad results!!
113   ArmyPrototypeMap::const_iterator it = d_armies.find(id);
114
115   // armyset does not exist
116   if (it == d_armies.end())
117     return 0;
118
119   // index too large
120   if (index >= (*it).second.size())
121     return 0;
122
123   return ((*it).second)[index];
124 }
125
126 ArmyProto* Armysetlist::getScout(guint32 id) const
127 {
128   // always use ArmyProtoMap::find for searching, else a default entry is 
129   // created, which can produce really bad results!!
130   ArmyPrototypeMap::const_iterator it = d_armies.find(id);
131
132   // armyset does not exist
133   if (it == d_armies.end())
134     return 0;
135
136   return ((*it).second)[0];
137 }
138
139 guint32 Armysetlist::getSize(guint32 id) const
140 {
141   ArmyPrototypeMap::const_iterator it = d_armies.find(id);
142
143   // armyset does not exist
144   if (it == d_armies.end())
145     return 0;
146
147   return (*it).second.size();
148 }
149
150 std::list<std::string> Armysetlist::getNames()
151 {
152   std::list<std::string> names;
153   for (iterator it = begin(); it != end(); it++)
154     names.push_back((*it)->getName());
155   return names;
156 }
157
158 std::list<std::string> Armysetlist::getNames(guint32 tilesize)
159 {
160   std::list<std::string> names;
161   for (iterator it = begin(); it != end(); it++)
162     if ((*it)->getTileSize() == tilesize)
163       names.push_back((*it)->getName());
164   return names;
165 }
166
167 std::string Armysetlist::getName(guint32 id) const
168 {
169   NameMap::const_iterator it = d_names.find(id);
170
171   // armyset does not exist
172   if (it == d_names.end())
173     return 0;
174
175   return (*it).second;
176 }
177
178 Armyset *Armysetlist::loadArmyset(std::string name)
179 {
180   debug("Loading armyset " <<name);
181   Armyset *armyset = Armyset::create(name);
182   if (armyset == NULL)
183     return NULL;
184   if (armyset->validate() == false)
185     {
186       cerr << "Error!  armyset: `" << armyset->getName() << 
187         "' is invalid.  Skipping." << endl;
188       delete armyset;
189       return NULL;
190     }
191   if (d_armysetids.find(armyset->getId()) != d_armysetids.end())
192     {
193       Armyset *a = (*d_armysetids.find(armyset->getId())).second;
194       cerr << "Error!  armyset: `" << armyset->getName() << 
195         "' shares a duplicate armyset id with `" << 
196         a->getConfigurationFile() << "'.  Skipping." << endl;
197       delete armyset;
198       return NULL;
199       return false;
200     }
201   return armyset;
202 }
203
204 PixMask* Armysetlist::getShipPic (guint32 id)
205 {
206   for (iterator it = begin(); it != end(); it++)
207     {
208       if ((*it)->getId() == id)
209         return (*it)->getShipPic();
210     }
211   return NULL;
212 }
213
214 PixMask* Armysetlist::getShipMask (guint32 id)
215 {
216   for (iterator it = begin(); it != end(); it++)
217     {
218       if ((*it)->getId() == id)
219         return (*it)->getShipMask();
220     }
221   return NULL;
222 }
223
224 guint32 Armysetlist::getTileSize(guint32 id)
225 {
226   for (iterator it = begin(); it != end(); it++)
227     {
228       if ((*it)->getId() == id)
229         return (*it)->getTileSize();
230     }
231   return 0;
232 }
233
234 PixMask* Armysetlist::getBagPic (guint32 id)
235 {
236   for (iterator it = begin(); it != end(); it++)
237     {
238       if ((*it)->getId() == id)
239         return (*it)->getBagPic();
240     }
241   return NULL;
242 }
243
244 PixMask* Armysetlist::getStandardPic (guint32 id)
245 {
246   for (iterator it = begin(); it != end(); it++)
247     {
248       if ((*it)->getId() == id)
249         return (*it)->getStandardPic();
250     }
251   return NULL;
252 }
253
254 PixMask* Armysetlist::getStandardMask (guint32 id)
255 {
256   for (iterator it = begin(); it != end(); it++)
257     {
258       if ((*it)->getId() == id)
259         return (*it)->getStandardMask();
260     }
261   return NULL;
262 }
263
264 void Armysetlist::getSizes(std::list<guint32> &sizes)
265 {
266   for (iterator i = begin(); i != end(); i++)
267     {
268       if (find (sizes.begin(), sizes.end(), (*i)->getTileSize()) == sizes.end())
269         sizes.push_back((*i)->getTileSize());
270     }
271 }
272
273 guint32 Armysetlist::getArmysetId(std::string armyset, guint32 tilesize)
274 {
275   return d_ids[String::ucompose("%1 %2", armyset, tilesize)];
276 }
277
278 std::string Armysetlist::getArmysetDir(std::string name, guint32 tilesize)
279 {
280   return getArmyset(getArmysetId(name, tilesize))->getSubDir();
281 }
282
283 int Armysetlist::getNextAvailableId(int after)
284 {
285   std::list<guint32> ids;
286   std::list<std::string> armysets = Armyset::scanSystemCollection();
287   //there might be IDs in invalid armysets.
288   for (std::list<std::string>::const_iterator i = armysets.begin(); 
289        i != armysets.end(); i++)
290     {
291       Armyset *armyset = Armyset::create(*i);
292       if (armyset != NULL)
293         {
294           ids.push_back(armyset->getId());
295           delete armyset;
296         }
297     }
298   armysets = Armyset::scanUserCollection();
299   for (std::list<std::string>::const_iterator i = armysets.begin(); 
300        i != armysets.end(); i++)
301     {
302       Armyset *armyset = Armyset::create(*i);
303       if (armyset != NULL)
304         {
305           ids.push_back(armyset->getId());
306           delete armyset;
307         }
308     }
309   for (guint32 i = after + 1; i < 1000000; i++)
310     {
311       if (find(ids.begin(), ids.end(), i) == ids.end())
312         return i;
313     }
314   return -1;
315 }
316
317 void Armysetlist::instantiateImages()
318 {
319   for (iterator it = begin(); it != end(); it++)
320     (*it)->instantiateImages();
321 }
322
323 void Armysetlist::uninstantiateImages()
324 {
325   for (iterator it = begin(); it != end(); it++)
326     (*it)->uninstantiateImages();
327 }
328
329 Armyset *Armysetlist::getArmyset(guint32 id) 
330 {
331   if (d_armysetids.find(id) == d_armysetids.end())
332     return NULL;
333   return d_armysetids[id];
334 }
335
336 Armyset *Armysetlist::getArmyset(std::string dir) 
337
338   if (d_armysets.find(dir) == d_armysets.end())
339     return NULL;
340   return d_armysets[dir];
341 }
342 bool Armysetlist::addToPersonalCollection(Armyset *armyset, std::string &new_subdir, guint32 &new_id)
343 {
344   //do we already have this one?
345
346   if (getArmyset(armyset->getSubDir()) == getArmyset(armyset->getId()) &&
347       getArmyset(armyset->getSubDir()) != NULL)
348     {
349       armyset->setDirectory(getArmyset(armyset->getId())->getDirectory());
350       return true;
351     }
352
353   //if the subdir conflicts with any other subdir, then change it.
354   if (getArmyset(armyset->getSubDir()) != NULL)
355     {
356       bool found = false;
357       for (int count = 0; count < 100; count++)
358         {
359           new_subdir = String::ucompose("%1%2", armyset->getSubDir(), count);
360           if (getArmyset(new_subdir) == NULL)
361             {
362               found = true;
363               break;
364             }
365         }
366       if (found == false)
367         return false;
368       armyset->setSubDir(new_subdir);
369     }
370   else
371     new_subdir = armyset->getSubDir();
372
373   //if the id conflicts with any other id, then change it
374   if (getArmyset(armyset->getId()) != NULL)
375     {
376       new_id = Armysetlist::getNextAvailableId(armyset->getId());
377       armyset->setId(new_id);
378     }
379   else
380     new_id = armyset->getId();
381
382   //make the directory where the armyset is going to live.
383   std::string directory = 
384     File::getUserArmysetDir() + armyset->getSubDir() + "/";
385
386   if (File::create_dir(directory) == false)
387     return false;
388
389   //okay now we copy the image files into the new directory 
390   std::list<std::string> files;
391   armyset->getFilenames(files);
392   for (std::list<std::string>::iterator it = files.begin(); it != files.end();
393        it++)
394     File::copy(armyset->getFile(*it), directory + *it);
395
396   //save out the armyset file
397   armyset->setDirectory(directory);
398   XML_Helper helper(armyset->getConfigurationFile(), std::ios::out, false);
399   armyset->save(&helper);
400   helper.close();
401       
402   add(armyset);
403   return true;
404 }
405
406 Armyset *Armysetlist::import(Tar_Helper *t, std::string f, bool &broken)
407 {
408   std::string filename = t->getFile(f, broken);
409   Armyset *armyset = Armyset::create(filename);
410   assert (armyset != NULL);
411   armyset->setSubDir(File::get_basename(f));
412
413   //extract all the files and remember where we extracted them
414   std::list<std::string> delfiles;
415   delfiles.push_back(filename);
416   std::list<std::string> files;
417   armyset->getFilenames(files);
418   for (std::list<std::string>::iterator i = files.begin(); i != files.end(); i++)
419     {
420       std::string b = *i + ".png";
421       std::string file = t->getFile(*i + ".png", broken);
422     delfiles.push_back (file);
423     }
424
425   std::string subdir = "";
426   guint32 id = 0;
427   addToPersonalCollection(armyset, subdir, id);
428
429   for (std::list<std::string>::iterator it = delfiles.begin(); it != delfiles.end(); it++)
430     File::erase(*it);
431   return armyset;
432
433 }
434