1 /*
2  Xye License (it is a PNG/ZLIB license)
3 
4 Copyright (c) 2006 Victor Hugo Soliz Kuncar
5 
6 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
9 
10     1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
11 
12     2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 
14     3. This notice may not be removed or altered from any source distribution.
15 
16 */
17 #include "options.h"
18 #include "gen.h"
19 #include "xye.h"
20 #include "browser.h"
21 
22 #include<iostream>
23 #include<fstream>
24 #include<map>
25 #include<algorithm>
26 #include<string>
27 
28 using std::string;
29 
30 namespace options
31 {
32 //With respect of ~
33 const char* XDG_DEFAULT_DATA_HOME = "/.local/share";
34 const char* XDG_DEFAULT_CONFIG_HOME = "/.config";
35 
36 
37 
38 struct parsedSkinFile;
39 void LoadColors(parsedSkinFile & ps);
40 void LoadLevelFile();
41 void LoadSkinFile(const char* file);
42 
43 // Public variables:
44     bool  xyeDirectionSprites;
45     string Dir;
46 
47     SDL_Color OneWayDoorColor;
48     SDL_Color ForceArrowColor;
49     SDL_Color EarthColor;
50     SDL_Color FloorColor;
51     SDL_Color WallColor      [XYE_WALL_VARIATIONS];
52     SDL_Color WallSpriteColor[XYE_WALL_VARIATIONS];
53 
54     SDL_Color HintColor;
55 
56     SDL_Color BFColor[6];
57     SDL_Color BKColor[6];
58 
59     SDL_Color LevelMenu_info;
60     SDL_Color LevelMenu_selected;
61     SDL_Color LevelMenu_selectederror;
62     SDL_Color LevelMenu_menu;
63     SDL_Color LevelMenu_menutext;
64     SDL_Color LevelMenu_selectedtext;
65     SDL_Color LevelMenu_infotext;
66 
67     string ExecutablePath;
68 
69 
70 const int MAX_FILENAMES_TO_REMEMBER = 100;
71 string currentSkinFile;
72 bool disableLevelColors = false;
73 
LevelColorsDisabled()74 bool LevelColorsDisabled()
75 {
76     return disableLevelColors;
77 }
78 
79 
80 //for simplicity's sake I copied it, hopefully we won't ever change this enum
81 enum blockcolor
82 {
83     B_YELLOW=0,
84     B_RED=1,
85     B_BLUE=2,
86     B_GREEN=3,
87     B_PURPLE=4
88 };
89 
90 string datahomefolder;
91 string confighomefolder;
92 bool haspickedtheme=false;
93 bool enundo=false;
94 bool bini=false;
95 
96 /*TiXmlDocument* cnf;
97 TiXmlDocument* skin;
98 TiXmlElement* ele;
99 TiXmlElement* skinele;
100 */
101 
102 
103 
104 
105 
106 string LevelFile;
107 char* Texture;
108 string WindowIcon;
109 char* LuminosityTexture;
110 char* Font;
111 char* FontBold;
112 
113 int FontSize=0;
114 int FontBoldSize=0;
115 
116 unsigned char r,g,b;
117 int GridSize;
118 unsigned int lvnum;
119 
120 
121 
122 // Used for saving level file numbers and recent level files...
123 int MemTime = 0;
124 typedef std::map< std::pair<int, string>, int> memmap;
125         memmap MemFileLevelNumber;
126         std::map< string, int> MemFileLevelTime;
127 
128 
129 std::map< std::pair<string,int> , string > MemSavedGame;
130 
131 //--------
132 // skin parser
133 struct parsedSkinFile
134 {
135     string sprites;
136     string windowicon;
137     string lum;
138     string font;
139     string boldFont;
140     string author;
141     string description;
142     string title;
143     bool   directionSprites;
144     int    ttfSize;
145     int    gridSize;
146 
147     SDL_Color OneWayDoorColor;
148     SDL_Color ForceArrowColor;
149     SDL_Color EarthColor;
150     SDL_Color FloorColor;
151     SDL_Color WallColor      [XYE_WALL_VARIATIONS];
152     SDL_Color WallSpriteColor[XYE_WALL_VARIATIONS];
153 
154     SDL_Color BFColor[6];
155     SDL_Color BKColor[6];
156 
157     SDL_Color HintColor;
158 
159     SDL_Color LevelMenu_info;
160     SDL_Color LevelMenu_selected;
161     SDL_Color LevelMenu_selectederror;
162     SDL_Color LevelMenu_menu;
163     SDL_Color LevelMenu_menutext;
164     SDL_Color LevelMenu_selectedtext;
165     SDL_Color LevelMenu_infotext;
166 
167 };
168 
tryParseColorOptions(TiXmlElement * skn,SDL_Color * c,char type,char bc)169 bool tryParseColorOptions(TiXmlElement* skn, SDL_Color* c,char type,char bc)
170 {
171     if (!skn) return false;
172     TiXmlElement* tem=skn->FirstChildElement("color");
173     const char* e1,*e2;
174     char alt= bc-'A'+'a';
175     while (tem)
176     {
177         e1=tem->Attribute("bc");
178         if (e1 && ((*e1==bc) || (*e1==alt)) )
179         {
180             e2=tem->Attribute("type");
181             if (e2 && (*e2==type))
182             {
183                 string quo;
184                 int i=0;
185                 quo=tem->Attribute("red");TryS2I(quo,i);c->r=i;
186                 quo=tem->Attribute("green");TryS2I(quo,i);c->g=i;
187                 quo=tem->Attribute("blue");TryS2I(quo,i);c->b=i;
188 
189 
190                 return true;
191             }
192         }
193 
194         tem=tem->NextSiblingElement("color");
195     }
196     return false;
197 }
198 
TryLoadLevelMenuColor(TiXmlElement * levelmenu,const char * name,SDL_Color & c,Uint8 dR,Uint8 dG,Uint8 dB)199 void TryLoadLevelMenuColor(TiXmlElement* levelmenu, const char* name, SDL_Color & c, Uint8 dR, Uint8 dG, Uint8 dB)
200 {
201     if(levelmenu==NULL)
202     {
203         c.r=dR;
204         c.g=dG;
205         c.b=dB;
206     }
207     else
208     {
209         TiXmlElement* el=levelmenu->FirstChildElement(name);
210         if(el!=NULL)
211         {
212             string quo;
213             int i;
214             quo=el->Attribute("red");TryS2I(quo,i);c.r=i;
215             quo=el->Attribute("green");TryS2I(quo,i);c.g=i;
216             quo=el->Attribute("blue");TryS2I(quo,i);c.b=i;
217         }
218         else
219         {
220             c.r=dR;
221             c.g=dG;
222             c.b=dB;
223         }
224     }
225     c.unused=255;
226 }
227 
parseSkinMenuColors(TiXmlElement * ele,parsedSkinFile & ps)228 string parseSkinMenuColors(TiXmlElement* ele, parsedSkinFile & ps) {
229     TiXmlElement* el=ele->FirstChildElement("levelmenu");
230     TryLoadLevelMenuColor(el,"info"         ,ps.LevelMenu_info         ,239,235,231);
231     TryLoadLevelMenuColor(el,"selected"     ,ps.LevelMenu_selected     ,250,211,150);
232     TryLoadLevelMenuColor(el,"selectederror",ps.LevelMenu_selectederror,255,  0,  0);
233     TryLoadLevelMenuColor(el,"menu"         ,ps.LevelMenu_menu         ,255,255,255);
234     TryLoadLevelMenuColor(el,"menutext"     ,ps.LevelMenu_menutext     ,  0,  0,  0);
235     TryLoadLevelMenuColor(el,"selectedtext" ,ps.LevelMenu_selectedtext ,  0,  0,  0);
236     TryLoadLevelMenuColor(el,"infotext"     ,ps.LevelMenu_infotext     ,  0,  0,  0);
237 
238     return "";
239 }
240 
parseMiscColorOptions(TiXmlElement * skn,parsedSkinFile & ps)241 string parseMiscColorOptions(TiXmlElement* skn, parsedSkinFile & ps) {
242     TiXmlElement* tem=skn->FirstChildElement("color");
243     while (tem!=NULL)
244     {
245         int variation = -1;
246         tem->QueryIntAttribute("variation", &variation);
247         const char* e1 = tem->Attribute("type");
248         SDL_Color * c=NULL;
249         int n = 1;
250 
251         if( (e1!=NULL) && (string(e1)=="WALL") )
252         {
253             c=ps.WallColor;
254             if(variation==-1) n=XYE_WALL_VARIATIONS;
255             else c+=variation;
256         }
257         else if( (e1!=NULL) && (string(e1)=="WALL_SPRITE") )
258         {
259             c=ps.WallSpriteColor;
260             if(variation==-1) n=XYE_WALL_VARIATIONS;
261             else c+=variation;
262         }
263         else if ( (e1!=NULL) && (string(e1)=="ONEWAYDOOR" ) )
264         {
265             c=&ps.OneWayDoorColor;
266             n=1;
267         }
268         else if ( (e1!=NULL) && (string(e1)=="FORCEARROW" ) )
269         {
270             c=&ps.ForceArrowColor;
271             n=1;
272         }
273         else if ( (e1!=NULL) && (string(e1)=="EARTH" ) )
274         {
275             c=&ps.EarthColor;
276             n=1;
277         }
278         else if ( (e1!=NULL) && (string(e1)=="FLOOR" ) )
279         {
280             c=&ps.FloorColor;
281             n=1;
282         }
283         else if ( (e1!=NULL) && (string(e1)=="HINT" ) )
284         {
285             c=&ps.HintColor;
286             n=1;
287         }
288 
289         if(c!=NULL)
290         {
291             for (int j=0; j<n; j++)
292             {
293 
294                 string quo;
295                 int i=0;
296                 quo=tem->Attribute("red");TryS2I(quo,i);c[j].r=i;
297                 quo=tem->Attribute("green");TryS2I(quo,i);c[j].g=i;
298                 quo=tem->Attribute("blue");TryS2I(quo,i);c[j].b=i;
299                 c[j].unused=255;
300             }
301         }
302         tem=tem->NextSiblingElement("color");
303     }
304     return "";
305 
306 }
307 
308 
parseSkinInformation(TiXmlElement * ele,parsedSkinFile & ps)309 void parseSkinInformation(TiXmlElement* ele, parsedSkinFile & ps) {
310     TiXmlElement* tem=ele->FirstChildElement("author");
311     if (tem!=NULL) {
312         if (tem->GetText() != NULL) {
313             ps.author = tem->GetText();
314         }
315     }
316     tem=ele->FirstChildElement("description");
317     if (tem!=NULL) {
318         if (tem->GetText() != NULL) {
319             ps.description = tem->GetText();
320         }
321     }
322     tem=ele->FirstChildElement("title");
323     if (tem!=NULL) {
324         if (tem->GetText() != NULL) {
325             ps.title = tem->GetText();
326         }
327     }
328 
329 }
330 
parseSkinColors(TiXmlElement * ele,parsedSkinFile & ps)331 string parseSkinColors(TiXmlElement* ele, parsedSkinFile & ps) {
332     string tm = parseSkinMenuColors(ele, ps);
333     if(tm != "") {
334         return tm;
335     }
336     int i;
337     char cname[6];
338     cname[B_YELLOW]='Y';
339     cname[B_BLUE]='B';
340     cname[B_GREEN]='G';
341     cname[B_RED]='R';
342     cname[B_PURPLE]='P';
343     cname[5]='W';
344 
345     //defaults:
346     for (int i=0; i<XYE_WALL_VARIATIONS; i++)
347     {
348         ps.WallSpriteColor[i].r = ps.WallSpriteColor[i].g = ps.WallSpriteColor[i].b = 192;
349         ps.WallSpriteColor[i].unused = 255;
350         ps.WallColor[i].r = ps.WallColor[i].g = ps.WallColor[i].b = ps.WallColor[i].unused = 255;
351     }
352     ps.OneWayDoorColor.r = 255, ps.OneWayDoorColor.g = ps.OneWayDoorColor.b = ps.OneWayDoorColor.unused = 0 ;
353     ps.ForceArrowColor.r = 100, ps.ForceArrowColor.g = 200, ps.ForceArrowColor.b = 200, ps.ForceArrowColor.unused = 0 ;
354     ps.EarthColor.r = ps.EarthColor.g = 255, ps.EarthColor.b = ps.EarthColor.unused = 0 ;
355     ps.FloorColor.r = ps.FloorColor.g = ps.FloorColor.b = 255, ps.FloorColor.unused = 0 ;
356     ps.HintColor.r = ps.HintColor.g = 255, ps.HintColor.b = 200, ps.HintColor.unused = 0;
357     tm = parseMiscColorOptions(ele,ps);
358     if(tm != "") {
359         return "";
360     }
361     for (i=0;i<6;i++)
362     {
363         if (! tryParseColorOptions(ele, ps.BKColor+i, 'B', cname[i] ) )
364         switch(i)
365         {
366             case 5:
367                 ps.BKColor[i].r=255;
368                 ps.BKColor[i].g=255;
369                 ps.BKColor[i].b=255;
370                 break;
371             case(B_YELLOW):
372                 ps.BKColor[i].r=255;
373                 ps.BKColor[i].g=255;
374                 ps.BKColor[i].b=0;
375                 break;
376 
377             case(B_RED):
378                 ps.BKColor[i].r=255;
379                 ps.BKColor[i].g=0;
380                 ps.BKColor[i].b=0;
381                 break;
382 
383             case(B_BLUE):
384                 ps.BKColor[i].r=0;
385                 ps.BKColor[i].g=0;
386                 ps.BKColor[i].b=255;
387                 break;
388             case(B_PURPLE):
389                 ps.BKColor[i].r = 128;
390                 ps.BKColor[i].g = 0;
391                 ps.BKColor[i].b = 128;
392                 break;
393 
394             default: //green
395                 ps.BKColor[i].r=0;
396                 ps.BKColor[i].g=170;
397                 ps.BKColor[i].b=0;
398         }
399         ps.BKColor[i].unused=255;
400         if (! tryParseColorOptions(ele, ps.BFColor+i, 'F', cname[i]   ))
401         switch(i)
402         {
403             case 5:
404                 ps.BFColor[i].r=0;
405                 ps.BFColor[i].g=0;
406                 ps.BFColor[i].b=0;
407                 break;
408 
409             case(B_YELLOW):
410                 ps.BFColor[i].r=255;
411                 ps.BFColor[i].g=0;
412                 ps.BFColor[i].b=0;
413                 break;
414 
415 
416             case(B_RED):
417                 ps.BFColor[i].r=255;
418                 ps.BFColor[i].g=255;
419                 ps.BFColor[i].b=0;
420                 break;
421 
422             case(B_BLUE):
423                 //
424                 ps.BFColor[i].r=0;
425                 ps.BFColor[i].g=255;
426                 ps.BFColor[i].b=255;
427                 break;
428 
429             case(B_PURPLE):
430                 ps.BFColor[i].r = 255;
431                 ps.BFColor[i].g = 255;
432                 ps.BFColor[i].b = 255;
433                 break;
434 
435             default: //Green
436                 //
437                 ps.BFColor[i].r=255;
438                 ps.BFColor[i].g=255;
439                 ps.BFColor[i].b=255;
440                 break;
441 
442         }
443         ps.BFColor[i].unused=255;
444 
445     }
446 
447     return "";
448 }
449 
parseSkinFile(const char * filename,parsedSkinFile & ps)450 string parseSkinFile(const char*filename, parsedSkinFile & ps)
451 {
452     bool correct = false;
453     TiXmlDocument skinxml(filename);
454     if ( ! skinxml.LoadFile()) {
455         cout << skinxml.ErrorDesc()<<endl;
456         return "Not a valid XML file.";
457     }
458     TiXmlElement* ele=skinxml.FirstChild("xyeskin")->ToElement();
459     if ( ele == NULL ) {
460         return "Not a valid Xye skin file.";
461     }
462     string err = parseSkinColors(ele, ps);
463     if ( err != "") {
464         return err;
465     }
466 
467     parseSkinInformation(ele,ps);
468 
469     const char* tm;
470     if (tm=ele->Attribute("sprites")) {
471         ps.sprites = fixpath(string("res/")+string(tm),true);
472         if  ( ! DoesFileExist(ps.sprites) ) {
473             return "Missing sprites file: "+ps.sprites;
474         }
475     } else {
476         return "No sprites file specified in skin XML.";
477     }
478     if (tm=ele->Attribute("icon")) {
479         ps.windowicon = fixpath(string("res/")+string(tm),true);
480         if  ( ! DoesFileExist(ps.windowicon) ) {
481             cout<< "Missing window icon file: "+ps.windowicon<<endl;
482             ps.windowicon = "";
483         }
484     } else {
485         cout<< "WRN: No window icon file specified in skin XML."<<endl;
486         ps.windowicon = "";
487     }
488 
489     if (tm=ele->Attribute("luminosity")) {
490         ps.lum = fixpath(string("res/")+string(tm),true);
491         if  ( ! DoesFileExist(ps.lum) ) {
492             return "Missing sprites luminosity file: "+ps.lum;
493         }
494     }
495     ps.directionSprites = false;
496     if (tm=ele->Attribute("xyedirections"))
497     {
498         ps.directionSprites = ( (strlen(tm) >= 1) && ((tm[0]=='y') || (tm[0]=='Y') ));
499     }
500     if (tm=ele->Attribute("fontfile")) {
501         ps.font = fixpath(string("res/")+string(tm),true);
502         if (! DoesFileExist(ps.font) ) {
503             return "Missing font file: "+ps.font;
504         }
505     } else {
506         return "No font file specified in skin XML.";
507     }
508     if (tm=ele->Attribute("boldfontfile")) {
509         ps.boldFont = fixpath(string("res/")+string(tm),true);
510         if (! DoesFileExist(ps.boldFont.c_str()) ) {
511             return "Missing bold font file: "+ps.boldFont;
512         }
513     } else {
514         ps.boldFont = ps.font;
515     }
516     ps.ttfSize = 0;
517     if (tm=ele->Attribute("truetypesize")) {
518         sscanf(tm,"%d",&ps.ttfSize);
519     }
520     if (tm=ele->Attribute("size")) {
521         if(sscanf(tm,"%d",&ps.gridSize)!=1) {
522             return "Invalid grid size.";
523         }
524     } else {
525         return "Unspecified grid size.";
526     }
527     return "";
528 }
529 
530 //---
531 
532 
533 
534 
Error(const char * msg)535 bool Error(const char* msg)
536 {
537     fprintf(stderr,"%s",msg);
538     throw msg;
539 }
540 
GetOptionsElement(TiXmlDocument * cnf)541 TiXmlElement* GetOptionsElement(TiXmlDocument* cnf)
542 {
543     TiXmlElement* ele;
544     if (cnf->LoadFile())
545     {
546         ele=cnf->FirstChildElement("options");
547         return(ele);
548     }
549  return NULL;
550 }
551 
fixpath(const string path,bool dohomecheck)552 string fixpath(const string path, bool dohomecheck)
553 {
554     #ifndef _WIN32
555         string home = GetDataHomeFolder();
556         if (dohomecheck && (home.length() != 0) ) {
557             string homeloc = home + "/" + path;
558             if (DoesFileExist(homeloc.c_str()) ) {
559                 return homeloc;
560             }
561         }
562     #endif
563     return Dir + "/" + path;
564 }
565 
Default()566 void Default()
567 {
568     LevelFile = "#browse";
569 }
570 
defaultxyeconf(const char * path,TiXmlElement * & options)571 TiXmlDocument* defaultxyeconf(const char* path,TiXmlElement *&options)
572 {
573     std::ofstream file;
574     file.open (path,std::ios::trunc | std::ios::out );
575     if (!file.is_open()) return NULL; //ouch just halt.
576 
577     file << "<?xml version='1.0' encoding='ISO-8859-1'?>\n"
578 "<!--xye config file-->\n"
579 "<options levelfile='#browse#' skinfile='default.xml' red='52' green='255' blue='52' />";
580 
581     file.close();
582     TiXmlDocument* r=new TiXmlDocument(path);
583     if (options=GetOptionsElement(r))
584         return r;
585     delete r;
586     return NULL;
587 }
588 
589 
590 
getxyeconf(TiXmlElement * & options)591 TiXmlDocument* getxyeconf(TiXmlElement *&options  )
592 {
593     printf("Looking for valid xyeconf.xml:\n");
594     string home = GetConfigHomeFolder(), loc;
595     redo:
596     TiXmlDocument* r;
597     if (home.length() != 0) {
598         loc = home + "/xyeconf.xml";
599     } else {
600         loc = Dir + "/xyeconf.xml";
601     }
602     r = new TiXmlDocument(loc.c_str());
603     if (options = GetOptionsElement(r) ) {
604         cout << "xyeconf.xml location: "<<loc<<endl;
605         return r;
606     }
607     delete r;
608     r = defaultxyeconf(loc.c_str(), options);
609     if (r != NULL) {
610         cout << "Generated default " << loc << endl;
611         return r;
612     } else if (home != "") {
613         home = "";
614         goto redo;
615     } else {
616         r = defaultxyeconf("./xyeconf.xml", options);
617         if (r == NULL) {
618             printf("Cannot get xyeconf.xml");
619             return NULL;
620         }
621         cout << "Generated default ./xyeconf.xml" << endl;
622         return r;
623     }
624 
625 
626 }
627 
strGetEnv(const char * var)628 string strGetEnv(const char* var)
629 {
630     const char* e = getenv(var);
631     if (e == NULL) {
632         return "";
633     } else{
634         return e;
635     }
636 }
637 
getXDGDataHome()638 string getXDGDataHome()
639 {
640     string home = strGetEnv("HOME");
641     string data = strGetEnv("XDG_DATA_HOME");
642     if ( (data == "") || ! DoesFileExist(data) ) {
643         if (home == "") {
644             return "";
645         }
646         data = home + XDG_DEFAULT_DATA_HOME;
647         if (! DoesFileExist(data)) {
648             return "";
649         }
650     }
651     return data;
652 }
653 
getXDGConfigHome()654 string getXDGConfigHome()
655 {
656     string home = strGetEnv("HOME");
657     string config = strGetEnv("XDG_CONFIG_HOME");
658     if ( (config == "") || ! DoesFileExist(config) ) {
659         if (home == "") {
660             return "";
661         }
662         config = home + XDG_DEFAULT_CONFIG_HOME;
663         if (! DoesFileExist(config)) {
664             return "";
665         }
666     }
667     return config;
668 
669 }
670 
671 
SetupXDGFolders()672 void SetupXDGFolders()
673 {
674     #ifndef _WIN32
675          datahomefolder = getXDGDataHome();
676          if (datahomefolder != "") {
677              string xyedatahomefolder = (datahomefolder+"/xye");
678              if (! TryToOpenFolder(xyedatahomefolder.c_str())  ) {
679                  MKDIR( xyedatahomefolder.c_str() , S_IRWXU);
680                  if (! TryToOpenFolder(xyedatahomefolder.c_str()) )  {
681                      datahomefolder = "";
682                  } else {
683                      string levelhomefolder = xyedatahomefolder+"/levels";
684                      string reshomefolder = xyedatahomefolder+"/res";
685                      if (! DoesFileExist(levelhomefolder) ) {
686                          cout << "Attempt to create '"<<levelhomefolder<<"'"<<endl;
687                          MKDIR( levelhomefolder.c_str() , S_IRWXU);;
688                      }
689                      if (! DoesFileExist(reshomefolder) ) {
690                          cout << "Attempt to create '"<<reshomefolder<<"'"<<endl;
691                          MKDIR( reshomefolder.c_str() , S_IRWXU);;
692                      }
693 
694                      datahomefolder = xyedatahomefolder;
695                  }
696              } else {
697                  datahomefolder = xyedatahomefolder;
698              }
699          }
700          //config only holds xyeconf.xml at this moment
701          confighomefolder = getXDGConfigHome();
702     #else
703         datahomefolder = "";
704         confighomefolder = "";
705     #endif
706 }
707 
Init()708 void Init()
709 {
710 
711 
712     if (bini) return;
713     SetupXDGFolders();
714 
715     disableLevelColors = false;
716     haspickedtheme = false;
717     enundo=true;
718     r=b=52;
719     g=255;
720     bini=true;
721     GridSize=20;
722     bini=true;
723     TiXmlElement* ele;
724     TiXmlDocument* cnf = getxyeconf(ele);
725 
726     if (!ele)
727     {
728          Default();
729          delete cnf;
730          return;
731     }
732 
733     const char * tm;
734     int i=1;
735 
736 
737     ele->QueryIntAttribute("levelnumber",&i);
738     lvnum=i;
739     i=255;
740 
741     if (tm=ele->Attribute("red")) r=atoi(tm);
742     if (tm=ele->Attribute("green")) g=atoi(tm);
743     if (tm=ele->Attribute("blue")) b=atoi(tm);
744 
745     if (tm=ele->Attribute("undo"))
746     {
747         if( tm[0] == 'N' || tm[0] == 'n' ) {
748             printf("NOTE: Undo is disabled!\n");
749             enundo=false;
750 
751         }
752     }
753 
754     if (tm=ele->Attribute("pickedtheme"))
755     {
756         if( tm[0] == 'Y' || tm[0] == 'y' ) {
757             haspickedtheme=true;
758 
759         }
760     }
761     if (tm=ele->Attribute("disablelevelcolors"))
762     {
763         if( tm[0] == 'Y' || tm[0] == 'y' ) {
764             disableLevelColors = true;
765         }
766     }
767 
768 
769 
770 
771     tm=ele->Attribute("levelfile");
772 
773     LevelFile = tm;
774 
775 
776     const char * sknfile = ele->Attribute("skinfile");
777     string skin;
778 
779     if (! sknfile) {
780          //12345678901234567
781          //"res/default.xml";
782          skin = fixpath("res/default.xml",true);
783          printf("No skin file selection found in xyeconf.xml, will use default.xml.\n");
784     } else {
785          skin = fixpath(string("res/") + sknfile, true);
786     }
787 
788     LoadSkinFile(skin.c_str() );
789 
790     //fix the paths of all the options that are file locations:
791 
792 
793     if (LevelFile != "#browse#") {
794         LevelFile = fixpath(string("levels/")+LevelFile, true);
795     }
796 
797 
798     LoadLevelFile();
799     delete cnf;
800 
801 }
802 
LoadMenuColors(parsedSkinFile & ps)803 void LoadMenuColors(parsedSkinFile & ps)
804 {
805     LevelMenu_info          = ps.LevelMenu_info;
806     LevelMenu_selected      = ps.LevelMenu_selected;
807     LevelMenu_selectederror = ps.LevelMenu_selectederror;
808     LevelMenu_menu          = ps.LevelMenu_menu;
809     LevelMenu_menutext      = ps.LevelMenu_menutext;
810     LevelMenu_selectedtext  = ps.LevelMenu_selectedtext;
811     LevelMenu_infotext      = ps.LevelMenu_infotext;
812 }
813 
LoadColors(parsedSkinFile & ps)814 void LoadColors(parsedSkinFile & ps)
815 {
816     LoadMenuColors(ps);
817     for (int i=0; i<6; i++) {
818         BFColor[i] = ps.BFColor[i];
819         BKColor[i] = ps.BKColor[i];
820     }
821     for (int i=0; i<XYE_WALL_VARIATIONS; i++) {
822         WallSpriteColor[i] = ps.WallSpriteColor[i];
823         WallColor[i] = ps.WallColor[i];
824     }
825     OneWayDoorColor = ps.OneWayDoorColor;
826     ForceArrowColor = ps.ForceArrowColor;
827     EarthColor = ps.EarthColor;
828     FloorColor = ps.FloorColor;
829     HintColor = ps.HintColor;
830 }
831 
832 
833 void PerformLevelFileSave();
Clean()834 void Clean()
835 {
836     if (bini)
837     {
838         PerformLevelFileSave();
839         delete [] Texture;
840         delete [] LuminosityTexture;
841         delete [] Font;
842         delete [] FontBold;
843 
844         bini=false;
845 
846         MemTime = 0;
847         MemFileLevelNumber.clear();
848         MemFileLevelTime.clear();
849         MemSavedGame.clear();
850 
851     }
852 
853 }
854 
GetDir()855 string GetDir()
856 {
857     ! bini? Error("Attempt to call unitialized options"):0;
858     return Dir;
859 }
860 
861 
GetLevelFile()862 const char* GetLevelFile()
863 {
864     ! bini? Error("Attempt to call unitialized options"):0;
865     if(LevelFile == "#browse#") return NULL;
866     return (LevelFile.c_str() );
867 }
868 
869 
GetSpriteFile()870 const char* GetSpriteFile()
871 {
872     ! bini? Error("Attempt to call unitialized options"):0;
873     return (Texture);
874 }
875 
GetWindowIconFile()876 const string GetWindowIconFile()
877 {
878     ! bini? Error("Attempt to call unitialized options"):0;
879     return (WindowIcon);
880 }
881 
882 
GetLuminositySpriteFile()883 const char* GetLuminositySpriteFile()
884 {
885     ! bini? Error("Attempt to call unitialized options"):0;
886     return (LuminosityTexture);
887 }
888 
889 
GetFontFile()890 const char* GetFontFile()
891 {
892     ! bini? Error("Attempt to call unitialized options"):0;
893     return (Font);
894 }
895 
GetFontBoldFile()896 const char* GetFontBoldFile()
897 {
898     ! bini? Error("Attempt to call unitialized options"):0;
899     return (FontBold);
900 }
901 
GetFontSize()902 int GetFontSize()
903 {
904     ! bini? Error("Attempt to call unitialized options"):0;
905     return (FontSize);
906 }
907 
GetFontBoldSize()908 int GetFontBoldSize()
909 {
910     ! bini? Error("Attempt to call unitialized options"):0;
911     return (FontBoldSize);
912 }
913 
GetGridSize()914 int GetGridSize()
915 {
916     ! bini? Error("Attempt to call unitialized options"):0;
917     return GridSize;
918 }
919 
GetDataHomeFolder()920 const string& GetDataHomeFolder()
921 {
922     ! bini? Error("Attempt to call unitialized options"):0;
923     return datahomefolder;
924 }
GetConfigHomeFolder()925 const string& GetConfigHomeFolder()
926 {
927     ! bini? Error("Attempt to call unitialized options"):0;
928     return confighomefolder;
929 }
930 
GetLevelsHomeFolder()931 string GetLevelsHomeFolder()
932 {
933     string f = options::GetDataHomeFolder();
934     if (f.length() != 0) {
935         return f + "/levels/";
936     } else {
937         return "";
938     }
939 }
940 
GetMyLevelsFolder()941 string GetMyLevelsFolder()
942 {
943     string f = GetLevelsHomeFolder();
944     if (f == "") {
945         return Dir+"/levels/mylevels/";
946     }
947     return f;
948 }
949 
GetResHomeFolder()950 string GetResHomeFolder()
951 {
952     string f = options::GetDataHomeFolder();
953     if (f.length() != 0) {
954         return f + "/res/";
955     } else {
956         return "";
957     }
958 }
959 
960 
Red()961 unsigned char Red()
962 {
963     return(r);
964 }
Green()965 unsigned char Green()
966 {
967     return(g);
968 }
Blue()969 unsigned char Blue()
970 {
971     return(b);
972 }
973 
974 bool options_saveignored = false;
975 
IgnoreLevelSave()976 void IgnoreLevelSave()
977 {
978     options_saveignored = true;
979 }
980 
981 
982 
983 
984 
SaveLevelFile(string filename,int levelNumber)985 void SaveLevelFile(string filename, int levelNumber)
986 {
987     if(filename == "") return;
988 
989     MemTime++;
990     string &s = filename;
991     if( MemFileLevelTime.count(s) == 1)
992     {
993         int t = MemFileLevelTime[s];
994         MemFileLevelTime[filename]  = MemTime;
995         MemFileLevelNumber.erase( MemFileLevelNumber.find( make_pair(t, s) ) );
996         MemFileLevelNumber[ make_pair(MemTime, s) ] =levelNumber;
997     }
998     else
999     {
1000         if( MemFileLevelTime.size() >= MAX_FILENAMES_TO_REMEMBER )
1001         {
1002             //remove the one with the lowest time
1003             std::pair<int,string> oldest = MemFileLevelNumber.begin()->first;
1004             MemFileLevelTime.erase( MemFileLevelTime.find(oldest.second) );
1005             MemFileLevelNumber.erase(MemFileLevelNumber.begin());
1006         }
1007         MemFileLevelTime[s] = MemTime;
1008         MemFileLevelNumber[ make_pair(MemTime,s) ] = levelNumber;
1009 
1010     }
1011 
1012     LevelFile = filename;
1013     lvnum = levelNumber;
1014 }
1015 
SaveLevelGame(string filename,int levelNumber,string moves)1016 void SaveLevelGame(string filename, int levelNumber, string moves)
1017 {
1018     if ( moves == string(moves.length(), '0') ) {
1019         ForgetLevelGame(filename,levelNumber);
1020     } else {
1021         pair<string,int> p = make_pair(filename, levelNumber);
1022         MemSavedGame[p] = moves;
1023     }
1024 }
1025 
LoadLevelGame(string filename,int levelNumber)1026 string LoadLevelGame(string filename, int levelNumber)
1027 {
1028     pair<string,int> p = make_pair(filename, levelNumber);
1029     map<pair<string,int>,string>::iterator q = MemSavedGame.find(p);
1030     if ( q == MemSavedGame.end() ) {
1031         return "";
1032     } else {
1033         return q->second;
1034     }
1035 }
ForgetLevelGame(string filename,int levelNumber)1036 void ForgetLevelGame(string filename, int levelNumber)
1037 {
1038     pair<string,int> p = make_pair(filename, levelNumber);
1039     map<pair<string,int>,string>::iterator q = MemSavedGame.find(p);
1040     if ( q != MemSavedGame.end() ) {
1041         MemSavedGame.erase(q);
1042     }
1043 
1044 }
1045 
1046 
GetLevelNumber(const char * filename)1047 unsigned int GetLevelNumber(const char* filename)
1048 {
1049     if(filename==NULL) return 1;
1050     string s=filename;
1051     if( MemFileLevelTime.count(s))
1052     {
1053         int t = MemFileLevelTime[s];
1054         return MemFileLevelNumber[ make_pair(t, s) ];
1055     }
1056     return 1;
1057 }
1058 
PerformLevelFileSave()1059 void PerformLevelFileSave()
1060 {
1061     if(options_saveignored) return;
1062     std::ofstream file;
1063     string home = GetDataHomeFolder();
1064     string path;
1065     if (home.length() > 0) {
1066         path = home+"/lastlevel.conf";
1067     } else {
1068         path = Dir+"/lastlevel.conf";
1069     }
1070     file.open (path.c_str(),std::ios::trunc | std::ios::out );
1071     if (!file.is_open()) return ; //ouch just halt.
1072 
1073     for ( memmap::iterator q = MemFileLevelNumber.begin(); q!=MemFileLevelNumber.end(); q++)
1074     {
1075         file<< q->first.second  << std::endl<< q->second <<std::endl;
1076     }
1077 
1078 
1079     file.close();
1080     if (home.length() > 0) {
1081         path = home+"/savedgames.conf";
1082     } else {
1083         path = Dir+"/savedgames.conf";
1084     }
1085 
1086     file.open (path.c_str(),std::ios::trunc | std::ios::out );
1087     if (!file.is_open()) return ; //ouch just halt.
1088 
1089     for ( map< pair<string,int>, string >::iterator q = MemSavedGame.begin(); q!=MemSavedGame.end(); q++)
1090     {
1091         file<< q->first.first<< std::endl << q->first.second << " "<<q->second <<std::endl;
1092     }
1093 
1094 
1095     file.close();
1096 
1097 
1098 }
1099 
1100 
1101 
GetSkinFile(bool stripPath)1102 string GetSkinFile(bool stripPath)
1103 {
1104     if (stripPath) {
1105         return StripPath(currentSkinFile);
1106     } else {
1107         return currentSkinFile;
1108     }
1109 
1110 }
SaveConfigFile()1111 void SaveConfigFile()
1112 {
1113     std::ofstream file;
1114     string home = GetConfigHomeFolder();
1115     string path;
1116     if (home.length() > 0) {
1117         path = home+"/xyeconf.xml";
1118     } else {
1119         path = Dir+"/xyeconf.xml";
1120     }
1121     file.open (path.c_str(),std::ios::trunc | std::ios::out );
1122     if (!file.is_open()) return ; //ouch just halt.
1123     file<<"<?xml version='1.0' encoding='ISO-8859-1'?>"<<endl;
1124     file<<"<!--xye config file-->"<<endl;
1125     file<<"<options levelfile='#browse#' ";
1126     file<<"skinfile='"<<GetSkinFile()<<"' ";
1127     file<<"red='"<<(int)Red()<<"' green='"<<(int)Green()<<"' blue='"<<(int)Blue()<<"' ";
1128     if(! UndoEnabled()) {
1129         file<<"undo='NO' ";
1130     }
1131     if ( HasConsciouslyChosenTheme() ) {
1132         file<<"pickedtheme='YES' ";
1133     }
1134     if ( disableLevelColors ) {
1135         file<<"disablelevelcolors='YES' ";
1136     }
1137 
1138     file<<"/>"<<endl;
1139 
1140     file.close();
1141 
1142 }
1143 
1144 
LoadLevelFile()1145 void LoadLevelFile()
1146 {
1147     MemTime = 0;
1148     MemFileLevelNumber.clear();
1149     MemFileLevelTime.clear();
1150 
1151 
1152     std::ifstream file, file2;
1153 
1154     // ............. Load saved games
1155     string home = GetDataHomeFolder();
1156     string path;
1157     if (home.length() > 0) {
1158         path = home + "/savedgames.conf";
1159     } else {
1160         path = Dir + "/savedgames.conf";
1161     }
1162     file.open (path.c_str(), std::ios::in );
1163     if ( file.is_open())
1164     {
1165         string LevelFile;
1166         int lvnum;
1167         string savedgame, empty;
1168 
1169         while( !file.eof()  )
1170         {
1171             getline(file,LevelFile);
1172             if(LevelFile=="") break;
1173             file>>lvnum;
1174             file>>savedgame;
1175 
1176             SaveLevelGame(LevelFile.c_str(), lvnum, savedgame);
1177             getline(file,LevelFile);
1178         }
1179     }
1180     file.close();
1181 
1182 
1183     if (home.length() > 0) {
1184         path = home + "/lastlevel.conf";
1185     } else {
1186         path = Dir + "/lastlevel.conf";
1187     }
1188     file2.open (path.c_str(), std::ios::in );
1189     if ( (!file2.is_open()) || (file2.eof() ) ) {
1190         printf("Could not open lastlevel.conf\n");
1191         LevelFile = "#browse#";
1192         lvnum = 0;
1193         return ; //ouch just halt.
1194     }
1195     string LevelFile;
1196     int lvnum;
1197     while( !file2.eof()  )
1198     {
1199         getline(file2,LevelFile);
1200         if(LevelFile=="") break;
1201         file2>>lvnum;
1202         SaveLevelFile(LevelFile.c_str(), lvnum);
1203         getline(file2,LevelFile);
1204     }
1205 
1206 }
1207 
1208 
1209 
UndoEnabled()1210 bool UndoEnabled() {
1211     return enundo;
1212 }
1213 
HasConsciouslyChosenTheme()1214 bool HasConsciouslyChosenTheme() {
1215     return haspickedtheme;
1216 }
1217 
1218 struct previewMaker {
1219     int w,h;
1220     int sz;
1221     parsedSkinFile* ps;
1222     Drawer* D;
1223     LuminositySprites ls;
1224 
1225     SDL_Surface * result;
previewMakeroptions::previewMaker1226     previewMaker(parsedSkinFile& ps, int w, int h) {
1227         this->w = w;
1228         this->h = h;
1229         this->ps = &ps;
1230         this->sz = ps.gridSize;
1231         ls.sprites = IMG_Load( ps.sprites.c_str() );
1232         if(ps.lum == "") {
1233             ls.luminosity = NULL;
1234         } else {
1235             ls.luminosity = IMG_Load( ps.lum.c_str() );
1236         }
1237 
1238         D = new Drawer(ls,0,0,sz,sz);
1239         result = SDL_CreateRGBSurface(0,w,h,32,SDL_ENDIAN32MASKS);
1240         Uint32 col = SDL_MapRGB(result->format,  ps.FloorColor );
1241         SDL_FillRect(result, 0, 0, w, h, col );
1242 
1243         draw(0,0,          1,0, Red(), Green(), Blue());
1244 
1245         //blocks
1246         draw(1,0,2,0, ps.BKColor[0]);
1247         draw(1,0,3,0, ps.BKColor[1]);
1248         draw(1,0,4,0, ps.BKColor[2]);
1249         draw(1,0,5,0, ps.BKColor[3]);
1250 
1251         //arrows
1252         draw(1,0,9,0, ps.BKColor[0]);
1253         draw(4,9,9,0, ps.BFColor[0]);
1254         draw(1,0,9,1, ps.BKColor[1]);
1255         draw(4,10,9,1, ps.BFColor[1]);
1256         draw(2,0,9,2, ps.BKColor[2]);
1257         draw(5,12,9,2, ps.BFColor[2]);
1258         draw(2,0,9,3, ps.BKColor[3]);
1259         draw(5,11,9,3, ps.BFColor[3]);
1260 
1261         //magnets
1262         draw(6,12,7,1);
1263 
1264         //wildcard
1265         draw(1,2,7,2);
1266 
1267 
1268         //gems
1269         draw(2,3+rand()%2,1,1);
1270         draw(3,3+rand()%2,4,1);
1271         draw(4,3+rand()%2,3,1);
1272         draw(5,3+rand()%2,2,1);
1273         draw(9,12+rand()%2,5,1);
1274 
1275         //monsters
1276         draw(16, rand()%2, 1,2);
1277         draw(19, rand()%2, 2,2);
1278         draw(19, 6+rand()%3, 3,2);
1279         draw(19, 9+rand()%3, 4,2);
1280         draw(10,12+rand()%2, 5,2);
1281         //robot
1282         draw(3,rand()%3, 1,3);
1283         //blackie
1284         draw(0,3, 2,3);
1285         draw(0,4, 2,3);
1286 
1287         //portal
1288         draw(8,rand()%3, 3,3, ps.BKColor[(int)(rand()%4)] );
1289 
1290         { //turner
1291             int d = rand()%4;
1292             draw(1,0,7,3, ps.BKColor[d]);
1293             draw(3,10,7,3, ps.BFColor[d]);
1294         }
1295 
1296         //pusher
1297         int d=rand()%2;
1298         draw(4+d,5, 6,0, ps.BKColor[0]);
1299         draw(4+d,7, 6,0, ps.BFColor[0]);
1300         d=rand()%2;
1301         draw(4+d,6, 7,0, ps.BKColor[2]);
1302         draw(4+d,8, 7,0, ps.BFColor[2]);
1303 
1304         //wall time !
1305         sz /= 2;
1306         draw(18,0, 0,0, ps.WallColor[0]);
1307         draw(19,0, 1,0, ps.WallColor[0]);
1308         for (int i=0; i<6; i++) {
1309             draw(24,i%2==0, 0,1+i, ps.WallColor[0]);
1310             draw(25,i%2==0, 1,1+i, ps.WallColor[0]);
1311         }
1312         draw(18,1, 0,7, ps.WallColor[0]);
1313         draw(19,1, 1,7, ps.WallColor[0]);
1314         //--
1315         draw(18,4, 12,2, ps.WallColor[2]);
1316         draw(19,4, 13,2, ps.WallColor[2]);
1317         for (int i=0; i<4; i++) {
1318             draw(24,4+(i%2==0), 12,3+i, ps.WallColor[2]);
1319             draw(25,4+(i%2==0), 13,3+i, ps.WallColor[2]);
1320         }
1321         draw(18,5, 12,7, ps.WallColor[2]);
1322         draw(19,5, 13,7, ps.WallColor[2]);
1323         //--
1324         draw(18,10, 16,0, ps.WallColor[5]);
1325         draw(19,10, 17,0, ps.WallColor[5]);
1326         for (int i=0; i<6; i++) {
1327             draw(24,10+(i%2==0), 16,1+i, ps.WallColor[5]);
1328             draw(25,10+(i%2==0), 17,1+i, ps.WallColor[5]);
1329         }
1330         draw(18,11, 16,7, ps.WallColor[5]);
1331         draw(19,11, 17,7, ps.WallColor[5]);
1332 
1333     }
~previewMakeroptions::previewMaker1334     ~previewMaker() {
1335         SDL_FreeSurface(ls.sprites);
1336         if(ls.luminosity != NULL) {
1337             SDL_FreeSurface(ls.luminosity);
1338         }
1339         delete D;
1340     }
1341 
drawoptions::previewMaker1342     void draw(int sx, int sy, int tx, int ty, SDL_Color col) {
1343         D->ChangeRect(sx*sz, sy*sz, sz, sz);
1344         D->SetColors(col,255);
1345         D->Draw(result, tx*sz, ty*sz);
1346     }
drawoptions::previewMaker1347     void draw(int sx, int sy, int tx, int ty, Uint8 r, Uint8 g, Uint8 b) {
1348         SDL_Color col;
1349         col.unused = 255;
1350         col.r = r, col.g = g, col.b = b;
1351         draw(sx,sy,tx,ty, col);
1352     }
drawoptions::previewMaker1353     void draw(int sx, int sy, int tx, int ty) {
1354         SDL_Color col;
1355         col.unused = col.r = col.g = col.b = 255;
1356         draw(sx,sy,tx,ty, col);
1357     }
1358 
1359 };
1360 
makeSkinPreview(parsedSkinFile & ps,int w,int h)1361 SDL_Surface* makeSkinPreview(parsedSkinFile & ps, int w, int h) {
1362     previewMaker pr(ps,w,h);
1363     return pr.result;
1364 }
1365 
GetSkinInformation(const char * file,SkinInformation & si)1366 bool GetSkinInformation(const char* file, SkinInformation & si)
1367 {
1368     si.title = "";
1369     si.author = "";
1370     si.description = "";
1371     si.title = "";
1372     if(si.preview != NULL) {
1373         SDL_FreeSurface(si.preview);
1374     }
1375     si.preview = NULL;
1376     parsedSkinFile ps;
1377     string error = parseSkinFile(file, ps);
1378     if(error!="") {
1379         si.description = error;
1380         return false;
1381     }
1382     //Make preview...
1383     haspickedtheme = true;
1384     si.preview= makeSkinPreview(ps, si.pw, si.ph);
1385     si.dimx = ps.gridSize * 30;
1386     si.dimy = ps.gridSize * 22;
1387     si.author = ps.author;
1388     si.description = ps.description;
1389     si.title = ps.title;;
1390     return true;
1391 }
1392 
LoadSkinFile(const char * file)1393 void LoadSkinFile(const char* file) {
1394     parsedSkinFile ps;
1395     string tms = parseSkinFile(file, ps);
1396     if(tms=="") {
1397         currentSkinFile = file;
1398         LoadColors(ps);
1399 
1400         WindowIcon = ps.windowicon;
1401 
1402         Texture = new char[ps.sprites.length()+1];
1403         strcpy(Texture, ps.sprites.c_str());
1404         if(ps.lum == "") {
1405             LuminosityTexture = NULL;
1406         } else {
1407             LuminosityTexture = new char[ps.lum.length()+1];
1408             strcpy(LuminosityTexture, ps.lum.c_str());
1409         }
1410         FontBoldSize = FontSize = ps.ttfSize;
1411         xyeDirectionSprites = ps.directionSprites;
1412         Font = new char[ps.font.length()+1];
1413         strcpy(Font, ps.font.c_str());
1414         FontBold = new char[ps.boldFont.length()+1];
1415         strcpy(FontBold, ps.boldFont.c_str());
1416         GridSize = ps.gridSize;
1417     } else {
1418         printf("%s",(tms+"\n").c_str());
1419     }
1420 }
1421 
1422 
ChangeSkinFile(const char * file,bool enableLevelColors)1423 void ChangeSkinFile(const char* file, bool enableLevelColors)
1424 {
1425     LoadSkinFile(file);
1426     disableLevelColors = ! enableLevelColors;
1427     game::RefreshGraphics();
1428     haspickedtheme = true;
1429     options::SaveConfigFile();
1430 }
1431 
1432 }
1433