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