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