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
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Library General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include "xmlhelper.h"
29 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
32 //they are only needed later for the expat callbacks
36 // forward declarations of the internally used functions
37 void start_handler(void* udata, const XML_Char* name, const XML_Char** atts);
38 void character_handler(void* udata, const XML_Char* s, int len);
39 void end_handler(void* udata, const XML_Char* name);
43 XML_Helper::XML_Helper(std::string filename, std::ios::openmode mode, bool zip)
44 : d_inbuf(0), d_outbuf(0), d_fout(0), d_fin(0), d_out(0), d_in(0),
45 d_last_opened(""), d_version(""), d_failed(false), d_zip(zip)
47 debug("Constructor called -- " << zip)
49 // always use a helper either for reading or for writing. Doing both
50 // is propably possible, but there is little point in using it anyway.
51 if ((mode & std::ios::in) && (mode & std::ios::out))
53 std::cerr << "XML_Helper: Either open file for reading or writing, not both, exiting\n";
57 //open input stream if required
58 if (mode & std::ios::in)
60 d_fin = new std::ifstream(filename.c_str(), std::ios::in);
65 std::cerr <<filename << ": Error opening file for reading. Exiting\n";
71 debug("IS ZIPPED ->" << tmp)
75 std::cerr << "File too short (<=1 byte), propably broken. Skipping load\n";
81 std::cout <<filename << ": The file is obfuscated, attempting to read it....\n";
82 d_fin->seekg (0,std::ios::end);
83 long length = d_fin->tellg();
84 d_fin->seekg (1, std::ios::beg);
89 debug(destlen1 << " -- "<< length);
92 length-=sizeof(uLongf);
94 char * buffer = (char*) malloc(length);
95 char * buf1 = (char *) malloc(destlen1);
97 d_fin->read(buffer,length);
100 uncompress((Bytef*)buf1,&destlen1,(Bytef*)buffer,length);
103 debug(destlen1 << " -- "<< length);
105 d_inbuf = new std::istringstream(buf1);
112 //std::cout <<filename <<_(": The file is not obfuscated, attempting to read it....\n");
113 d_fin->seekg(0, std::ios::beg);
118 if (mode & std::ios::out)
120 d_fout = new std::ofstream(filename.c_str(),
121 std::ios::out & std::ios::trunc);
124 std::cerr <<filename << ": Error opening file for writing. Exiting\n";
128 d_outbuf = new std::ostringstream();
133 XML_Helper::XML_Helper(std::ostream* output)
134 : d_inbuf(0), d_outbuf(0), d_fout(0), d_fin(0), d_out(0), d_in(0),
135 d_last_opened(""), d_version(""), d_failed(false), d_zip(false)
140 XML_Helper::XML_Helper(std::istream* input)
141 : d_inbuf(0), d_outbuf(0), d_fout(0), d_fin(0), d_out(0), d_in(0),
142 d_last_opened(""), d_version(""), d_failed(false), d_zip(false)
147 XML_Helper::~XML_Helper()
149 if (d_tags.size() != 0)
150 // should never happen unless there was an error
151 std::cerr << "XML_Helper: dtags not empty!!\n";
153 debug("Called destructor\n")
157 bool XML_Helper::begin(std::string version)
160 (*d_out) <<"<?xml version=\"1.0\"?>\n";
165 bool XML_Helper::openTag(std::string name)
169 std::cerr << "XML_Helper: no output stream given\n";
173 if ((name[0] == 'd') && (name[1] == '_'))
175 std::cerr <<name << ": The tag name starts with a \"d\". Not creating tag!\n";
181 // append the version strin got the first opened tag
183 (*d_out) <<"<" <<name <<" version=\"" <<d_version <<"\">\n";
185 (*d_out) <<"<" <<name <<">\n";
187 d_tags.push_front(name);
191 bool XML_Helper::closeTag()
195 std::cerr << "XML_Helper: no output stream given.\n";
199 std::string name = (*d_tags.begin());
201 //remove tag from list
205 (*d_out) <<"</" <<name <<">\n";
211 bool XML_Helper::saveData(std::string name, const Gdk::Color value)
213 //prepend a "d_" to show that this is a data tag
218 std::cerr << "XML_Helper: save_data with empty name\n";
223 std::cerr << "XML_Helper: no output stream given.\n";
230 r = value.get_red_p() * 255;
231 g = value.get_green_p() * 255;
232 b = value.get_blue_p() * 255;
233 snprintf(buf, sizeof(buf), "%02X", r);
234 std::string red = buf;
235 snprintf(buf, sizeof(buf), "%02X", g);
236 std::string green = buf;
237 snprintf(buf, sizeof(buf), "%02X", b);
238 std::string blue = buf;
240 (*d_out) <<"<" <<name <<">#" <<red <<green<<blue <<"</" <<name <<">\n";
244 bool XML_Helper::saveData(std::string name, std::string value)
246 //prepend a "d_" to show that this is a data tag
251 std::cerr << "XML_Helper: save_data with empty name\n";
256 std::cerr << "XML_Helper: no output stream given.\n";
261 (*d_out) <<"<" <<name <<">" <<value <<"</" <<name <<">\n";
265 bool XML_Helper::saveData(std::string name, int value)
267 //prepend a "d_" to show that this is a data tag
272 std::cerr << "XML_Helper: save_data with empty name\n";
277 std::cerr << "XML_Helper: no output stream given.\n";
282 (*d_out) <<"<" <<name <<">" <<value <<"</" <<name <<">\n";
286 bool XML_Helper::saveData(std::string name, guint32 value)
288 //prepend a "d_" to show that this is a data tag
293 std::cerr << "XML_Helper: save_data with empty name\n";
298 std::cerr << "XML_Helper: no output stream given.\n";
303 (*d_out) <<"<" <<name <<">" <<value <<"</" <<name <<">\n";
307 bool XML_Helper::saveData(std::string name, bool value)
309 //prepend a "d_" to show that this is a data tag
314 std::cerr << "XML_Helper: save_data with empty name\n";
319 std::cerr << "XML_Helper: no output stream given.\n";
324 s = (value? "true" : "false");
327 (*d_out) <<"<" <<name <<">" <<s <<"</" <<name <<">\n";
331 bool XML_Helper::saveData(std::string name, double value)
333 //prepend a "d_" to show that this is a data tag
338 std::cerr << "XML_Helper: save_data with empty name\n";
343 std::cerr << "XML_Helper: no output stream given.\n";
348 (*d_out) <<"<" <<name <<">" <<value <<"</" <<name <<">\n";
352 /* This is a wrapper for the AMD64 platform */
353 bool XML_Helper::saveData(std::string name, unsigned long int value)
355 return saveData(name, static_cast<guint32>(value));
357 /* End wrapper AMD64 */
359 bool XML_Helper::close()
366 debug("Saving game and obfuscating the Savefile.\n")
367 std::string tmp = d_outbuf->str();
370 long origlength = tmp.length();
371 uLongf ziplength = static_cast<uLongf>(origlength + (12+0.01*origlength));
372 char *buf1= (char*) malloc(ziplength);
374 int ret = compress2((Bytef*)buf1, &ziplength,
375 (Bytef*)tmp.c_str(), (uLong)origlength, 9);
376 debug("RET="<<ret<<" length=" << origlength << " destlen1=" << ziplength)
379 (*d_fout) << origlength;
381 d_fout->write(buf1, ziplength);
388 debug("destroyed d_outbuf")
389 ret=0;// to avoid warning
393 debug("I do not zip IT")
394 debug(_("Saving game without obfuscation.\n"))
396 std::string tmp = d_outbuf->str();
397 d_fout->write(tmp.c_str(), tmp.length());
402 debug("destroyed d_outbuf")
410 debug("destroyed d_inbuf")
433 void XML_Helper::addTabs()
435 for (unsigned int i = d_tags.size(); i > 0; i--)
440 bool XML_Helper::registerTag(std::string tag, XML_Slot callback)
442 //register tag as important
443 d_callbacks[tag] = callback;
449 bool XML_Helper::unregisterTag(std::string tag)
451 std::map<std::string, XML_Slot>::iterator it = d_callbacks.find(tag);
453 if (it == d_callbacks.end())
457 d_callbacks.erase(it);
461 bool XML_Helper::getData(Gdk::Color& data, std::string name)
463 //the data tags are stored with leading "d_", so prepend it here
466 std::map<std::string, std::string>::const_iterator it;
468 it = d_data.find(name);
470 if (it == d_data.end())
472 data.set_rgb_p(0,0,0);
473 std::cerr<<"XML_Helper::getData(Gdk::Color, \"" <<name <<"\") failed\n";
478 std::string value = (*it).second;
480 int retval = sscanf(value.c_str(), "%s", buf);
484 int red = 0, green = 0, blue = 0;
488 //must look like "#00FF33"
489 retval = sscanf(buf, "%c%02X%02X%02X", &hash, &red, &green, &blue);
495 //must look like "123 255 000"
496 retval = sscanf(value.c_str(), "%d%d%d", &red, &green, &blue);
499 if (red > 255 || red < 0 || green > 255 || green < 0 ||
500 blue > 255 || blue < 0)
503 data.set_rgb_p((float)red / 255.0, (float)green / 255.0,
504 (float)blue / 255.0);
508 bool XML_Helper::getData(std::string& data, std::string name)
510 //the data tags are stored with leading "d_", so prepend it here
513 std::map<std::string, std::string>::const_iterator it;
515 it = d_data.find(name);
517 if (it == d_data.end())
520 std::cerr<<"XML_Helper::getData(std::string, \"" <<name <<"\") failed\n";
530 bool XML_Helper::getData(bool& data, std::string name)
532 //the data tags are stored with leading "d_", so prepend it here
535 std::map<std::string, std::string>::const_iterator it;
536 it = d_data.find(name);
538 if (it == d_data.end())
540 std::cerr<<"XML_Helper::getData(bool, \"" <<name <<"\") failed\n";
545 if ((*it).second == "true")
551 if ((*it).second == "false")
560 bool XML_Helper::getData(int& data, std::string name)
562 //the data tags are stored with leading "d_", so prepend it here
565 std::map<std::string, std::string>::const_iterator it;
566 it = d_data.find(name);
568 if (it == d_data.end())
570 std::cerr<<"XML_Helper::getData(int, \"" <<name <<"\") failed\n";
575 data = atoi((*it).second.c_str());
579 bool XML_Helper::getData(guint32& data, std::string name)
581 //the data tags are stored with leading "d_", so prepend it here
584 std::map<std::string, std::string>::const_iterator it;
585 it = d_data.find(name);
587 if (it == d_data.end())
589 std::cerr<<"XML_Helper::getData(guint32, \"" <<name <<"\") failed\n";
594 data = static_cast<guint32>(atoi((*it).second.c_str()));
600 bool XML_Helper::getData(double& data, std::string name)
602 //the data tags are stored with leading "d_", so prepend it here
605 std::map<std::string, std::string>::const_iterator it;
606 it = d_data.find(name);
608 if (it == d_data.end())
610 std::cerr<<"XML_Helper::getData(double, \"" <<name <<"\") failed\n";
615 data = strtod((*it).second.c_str(), 0);
619 bool XML_Helper::parse()
621 if (!d_in || d_failed)
623 //what's the use of parsing no or incorrect input?
625 d_parser = XML_ParserCreate("utf-8");
627 //set handlers and data
628 XML_SetElementHandler(d_parser, start_handler, end_handler);
629 XML_SetCharacterDataHandler(d_parser, character_handler);
630 XML_SetUserData(d_parser, static_cast<void*>(this));
632 while (!d_in->eof() && !d_failed)
634 char* buffer = static_cast<char*>(XML_GetBuffer(d_parser,1000));
635 d_in->getline(buffer, 1000);
636 bool my_eof = d_in->eof();
638 if (!XML_ParseBuffer(d_parser, strlen(buffer), my_eof))
641 std::cerr <<"XML_Helper: error parsing xml document.\n";
642 std::cerr <<"Line " <<XML_GetCurrentLineNumber(d_parser);
643 std::cerr <<": " <<XML_ErrorString(XML_GetErrorCode(d_parser)) <<"\n";
644 std::cerr <<"Buffercontent = " << buffer << std::endl;
649 XML_ParserFree(d_parser);
654 //beginning with here is only internal stuff. Continue reading only if you are
655 //interested in the xml parsing. :)
657 /* Parsing works like this: We have three expat callback functions,
658 * startelementhandler, endelementhandler and cdatahandler.
659 * The startelement handler just calls XML_Helper::tag_open (an object is passed
660 * as data), the cdata element handler just sums up the cdata, and the end
661 * element handler calls XML_Helper::tag_close, giving it also the final cdata
662 * string (the string between opened tag and closed tag) to the XML_Helper.
663 * Since data is always stored like "<mydata>data_value</mydata>", having
664 * a startelementhandler encounter a non-null summed up cdata string is a
665 * serious error and results in a fail.
667 * Now the XML_Helper functions:
668 * tag_open looks if another important tag has already been opened last (and not
669 * called back). If so, it assumes that all important data has already been
670 * stored and calls the callback for the former tag. If not, it just goes on.
671 * last_opened is always set to the last opened tag marked as important.
672 * If tag_close is called, it is mostly for data. If cdata is != 0 it is some
673 * saved data. If the last_opened tag is the same as the closed tag (we disallow
674 * and thus ignore constructions like "<mytag> <mytag> </mytag> </mytag>" here,
675 * they are IMO pointless), we suppose that the callback has not been called yet
676 * and do it now. If not, then there has been another important tag on the way
677 * which has led tag_open to already call the callback.
680 bool XML_Helper::tag_open(std::string tag, std::string version, std::string lang)
685 //first of all, register the tag as opened
686 d_tags.push_front(tag);
691 //look if the tag starts with "d_". If so, it is a data tag without anything
692 //important in between
693 if ((tag[0] == 'd') && (tag[1] == '_'))
699 //first of all, look if another important tag has already been opened
700 //and call the appropriate callback if so
701 std::list<std::string>::iterator ls_it;
702 ls_it = d_tags.begin();
705 if ((ls_it != d_tags.end()) && (d_last_opened == (*ls_it)))
707 std::map<std::string, XML_Slot>::iterator it;
708 it = d_callbacks.find(*ls_it);
711 if (it != d_callbacks.end())
713 //make the callback (yes that is the syntax, overloaded "()")
714 bool retval = (it->second)(*ls_it, this);
717 std::cerr <<(*ls_it) << ": Callback for tag returned false. Stop parsing document.\n";
723 //clear d_data (we are just setting up a new tag)
733 bool XML_Helper::lang_check(std::string lang)
735 static char *envlang = getenv("LANG");
741 char *first_underscore = strchr (envlang, '_');
742 if (first_underscore)
744 if (strncmp (lang.c_str(), envlang, first_underscore - envlang) == 0)
750 bool XML_Helper::tag_close(std::string tag, std::string cdata)
755 //remove tag entry, there is nothing more to be done
758 if ((tag[0] == 'd') && (tag[1] == '_'))
760 // save the data (we close a data tag)
761 if (lang_check(d_lang[tag]))
763 return true; //data tags end here with their execution
766 if ((d_last_opened == tag))
767 //callback hasn't been called yet
769 std::map<std::string, XML_Slot>::iterator it;
770 it = d_callbacks.find(tag);
772 if (it != d_callbacks.end())
774 //make the callback (yes that is the syntax, overloaded "()")
775 bool retval = it->second(tag, this);
779 std::cerr <<tag <<": Callback for tag returned false. Stop parsing document.\n";
786 //clear d_data (we are just setting up a new tag)
793 //these functions are the expat callbacks. Their main purpose is to set up
794 //the parametres and call the tag_open and tag_close functions in XML_Helper.
796 //the start handler, call open_tag and do misc init stuff
797 void start_handler(void* udata, const XML_Char* name, const XML_Char** atts)
802 XML_Helper* helper = static_cast<XML_Helper*>(udata);
803 std::string version = "";
804 std::string lang = "";
806 //the only attribute we know and handle are version strings
807 if ((atts[0] != 0) && (std::string(atts[0]) == "version"))
808 version = std::string(atts[1]);
810 if ((atts[0] != 0) && (std::string(atts[0]) == "xml:lang"))
811 lang = std::string(atts[1]);
815 error = !helper->tag_open(std::string(name), version, lang);
818 //the cdata handler, just sums up the string s
819 void character_handler(void* udata, const XML_Char* s, int len)
824 char buffer[len+1]; //TODO: this is a gcc extension, very handy, but
825 //not neccessarily portable
827 strncpy(buffer, s, len);
830 //now add the string to the other one
831 my_cdata += std::string(buffer);
834 //the end handler: call tag_close and dosome cleanup
835 void end_handler(void* udata, const XML_Char* name)
840 XML_Helper* helper = static_cast<XML_Helper*>(udata);
842 error = !helper->tag_close(std::string(name), my_cdata);