1 // Copyright (C) 2002, 2003 Michael Bartl
2 // Copyright (C) 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2003, 2004, 2005 Andrea Paternesi
4 // Copyright (C) 2011, 2012, 2014 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 /** This class is intended to make xml handling easier. It offers
22   * save and load functions. You can initialise it either with a filename or
23   * a stream (to make dumping data also possible)
24   *
25   * Saving works like this:
26   * Open a new tag with openTag(), save your data with saveData() and close the
27   * tag with closeTag(). The output will look like:
28   *
29   * <map>
30   *     <maptile>
31   *         <d_x> 50 </d_x>
32   *         <d_y> 10 </d_y>
33   *         <d_type> 5 </d_type>
34   *     </maptile>
35   *     ...
36   * </map>
37   *
38   * map and maptile are opened tags, the tags x, y and type are saved data.
39   * ATTENTION: Always save data before you start opening up subtags!!!
40   * i.e. if you want to save some data concerning the tag map, save it _before_
41   * you open the first subtag (here maptile).
42   *
43   * The save code in LordsAWar follows a top-down hierarchy. As an example,
44   * The player's stacklist saves its data first, then calls
45   * the save functions of all the stacks. The stacks themselves call the save
46   * functions of their armies when they have stored their own data. The topmost
47   * object in the hierarchy is the GameScenario instance.
48   *
49   * Loading:
50   * First, you supply xml_helper with callback functions via registerTag.
51   *
52   * When you are ready, call parse. The parser will start until it finds a special
53   * tag (not starting with d_). It will then parse the next tags, assuming that
54   * these are data tags, until it finds the next special tag. When this is
55   * reached, it stops and calls the callback for the former special tag.
56   * Sometimes (e.g. in the case of a ruin being created and a stack tag) this
57   * callback will set up the callback for the next tag and demand some data.
58   * This data has already been stored and will be supplied on a request of
59   * getData(). That's all.
60   *
61   * Loading is also implemented in a hierarchical way. In the example above, the
62   * stacklist constructor is initialised with the XML_Helper instance. Then it
63   * registers an internal stacklist function as callback for the "stack" tags.
64   * This function is started when the first stack tag is encountered and creates
65   * a new Stack instance with the XML_Helper instance ...
66   *
67   * If this explanation was confusing, have a look at the loading and saving
68   * functions. They should make the point somewhat clearer.
69   */
70 
71 #pragma once
72 #ifndef XML_HELPER_H
73 #define XML_HELPER_H
74 
75 #include <gtkmm.h>
76 #include <string>
77 #include <iosfwd>
78 #include <fstream>
79 #include <list>
80 #include <map>
81 #include <sigc++/slot.h>
82 #include <libxml++/libxml++.h>
83 
84 class XML_Helper;
85 
86 typedef sigc::slot<bool, Glib::ustring, XML_Helper*> XML_Slot;
87 
88 //! XML handling class.
89 class XML_Helper: public xmlpp::SaxParser
90 {
91     public:
92 
93         static Glib::ustring xml_entity; // <?xml version=\"1.0\"?>
94 
95         /** The most common constructor reads or writes to a file
96           *
97           * @param filename     the filename where data read from/written to
98           * @param openmode     either std::ios::in for reading or out for writing
99           */
100         XML_Helper(Glib::ustring filename, std::ios::openmode mode);
101 
102         /** This constructor reads from a given input stream.
103           *
104           * @param input        the input stream to read from
105           */
106         XML_Helper(std::istream* input);
107 
108         /** This constructor writes to a given output stream.
109           *
110           * @param output       the output stream to write to
111           */
112         XML_Helper(std::ostream* output);
113         ~XML_Helper();
114 
115         /** Call this at the very beginning of the saving procedure. It
116           * initializes some items.
117           *
118           * @param version          the version number for the savegame
119           * @return true on success, false on error
120           */
121         bool begin(Glib::ustring version);
122 
123         /** Opens a new subtag
124           *
125           * @param name             the name of the subtag
126           * @return true on success, false otherwise
127           */
128         bool openTag(Glib::ustring name);
129 
130         //! Closes the most recently opened tag
131         bool closeTag();
132 
133         /** Save data
134           *
135           * There exist various save functions for different types of data. The
136           * data is enclosed in tags which are automatically prepended a d_ to
137           * distinguish them from the subdividing tags.
138           *
139           * @param identifier           the name for the data tag
140           * @param value                the data
141           * @return true on success, false otherwise
142           */
143         bool saveData(Glib::ustring identifier, const Glib::ustring value);
144         bool saveData(Glib::ustring identifier, const int value);
145         bool saveData(Glib::ustring identifier, const guint32 value);
146         bool saveData(Glib::ustring identifier, const bool value);
147         bool saveData(Glib::ustring identifier, const double value);
148         /* amd64 fix, UL: still neccessary?*/
149         bool saveData(Glib::ustring identifier, unsigned long int value);
150 	bool saveData(Glib::ustring identifier, const Gdk::RGBA value);
151 
152         /** Closes the reading/writing stream.
153           * @note As soon as you call this function, the object is dead with
154           * all streams cut. It is just here to force saving of files as the
155           * streams are also closed in the destructor.
156           */
157         bool close();
158 
159 
160         /** Registers a new tag handler
161           *
162           * Use this function to register new callbacks which are called when
163           * certain tags are encountered.
164           *
165           * @param tag          the name of the tag associated with the callback
166           * @param callback     a pointer to the callback function
167           * @return true on success, false otherwise
168           */
169         bool registerTag(Glib::ustring tag, XML_Slot callback);
170 
171         /** Provides cached data
172           *
173           * Use this function to get back the data. There are several functions
174           * for several types of data.
175           *
176           * @param data         a reference where the data is returned
177           * @param name         the name of the data tag
178           * @return true false if the data was not found or of wrong type
179           *
180           * @note For string data you can also specify if the data should be
181           * translated ro not.
182           */
183         bool getData(Glib::ustring& data, Glib::ustring name);
184         bool getData(bool& data, Glib::ustring name);
185         bool getData(int& data, Glib::ustring name);
186         bool getData(guint32& data, Glib::ustring name);
187         bool getData(double& data, Glib::ustring name);
188 	bool getData(Gdk::RGBA & data, Glib::ustring name);
189 
190         //! Returns the version number of the save file
getVersion()191         Glib::ustring getVersion() const {return d_version;}
192 
193         //! Use this function to start reading a file or stream
194         bool parseXML();
195 
196         static Glib::ustring get_top_tag(Glib::ustring filename);
197         static bool rewrite_version(Glib::ustring filename, Glib::ustring tag, Glib::ustring new_version);
198         static guint32 flagsFromString(Glib::ustring flags, guint32 (*flagStrToNum)(Glib::ustring));
199 
200 
201     protected:
202         virtual void on_start_element(const Glib::ustring& name,
203                                                                       const AttributeList& properties);
204         virtual void on_end_element(const Glib::ustring& name);
205         virtual void on_characters(const Glib::ustring& characters);
206     private:
207         /** Prepends a number of tags (depending on the number of opened tags)
208           * to a line. Used for beautification.
209           */
210         inline void addTabs();
211 
212 	bool lang_check(Glib::ustring lang);
213 
214         bool tag_open(Glib::ustring tag, Glib::ustring version, Glib::ustring lang);
215 
216         bool tag_close(Glib::ustring tag, Glib::ustring cdata = "");
217 
218         //streams, d_fout and d_fin are used when it comes to file
219         //handling, d_in and d_out are used when actually reading or
220         //writing data(either point to d_fout or d_fin or have a
221         //separate stream)
222         std::istringstream* d_inbuf;
223         std::ostringstream* d_outbuf;
224 
225         std::ofstream* d_fout;
226         std::ifstream* d_fin;
227         std::ostream* d_out;
228         std::istream* d_in;
229 
230         std::list<Glib::ustring> d_tags;
231 
232         std::map<Glib::ustring, XML_Slot> d_callbacks;
233         std::map<Glib::ustring, Glib::ustring> d_data;
234         std::map<Glib::ustring, Glib::ustring> d_lang;
235 
236         Glib::ustring d_last_opened;
237         Glib::ustring d_version;
238 
239         bool d_failed;
240         Glib::ustring my_cdata;
241         bool error;
242 };
243 
244 //! A helper class for grabbing the version of a game file.
245 class VersionLoader
246 {
247 public:
VersionLoader(Glib::ustring filename,Glib::ustring tag,Glib::ustring & version,bool & broken)248     VersionLoader(Glib::ustring filename, Glib::ustring tag, Glib::ustring &version, bool &broken)
249       {
250         std::ifstream in(filename.c_str());
251         if (in)
252           {
253             d_tag = tag;
254             XML_Helper helper(filename.c_str(), std::ios::in);
255             helper.registerTag(tag, sigc::mem_fun(*this, &VersionLoader::load));
256             bool retval = helper.parseXML();
257             if (!retval)
258               broken = true;
259             version = d_version;
260           }
261       }
load(Glib::ustring tag,XML_Helper * helper)262     bool load(Glib::ustring tag, XML_Helper* helper)
263       {
264         if (tag == d_tag)
265           {
266             d_version = helper->getVersion();
267             return true;
268           }
269         return false;
270       }
271 
272     Glib::ustring d_tag;
273     Glib::ustring d_version;
274 
275 };
276 
277 #endif //XML_HELPER_H
278