1 /*
2  * Copyright 2009-2016 Peter Kosyh <p.kosyh at gmail.com>
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24 
25 #include "externals.h"
26 #include "internals.h"
27 
28 
29 static int restart_needed = 0;
30 static int games_menu_from = 0;
31 static int themes_menu_page = 0;
32 
33 static int cur_lang = 0;
34 
35 int cur_menu = menu_main;
36 int top_menu = menu_main;
37 
38 char *UNKNOWN_ERROR = NULL;
39 char *ERROR_MENU = NULL;
40 char *WARNING_MENU = NULL;
41 char *SAVE_SLOT_EMPTY = NULL;
42 char *SELECT_LOAD_MENU = NULL;
43 char *AUTOSAVE_SLOT = NULL;
44 char *BROKEN_SLOT = NULL;
45 char *SELECT_SAVE_MENU = NULL;
46 char *MAIN_MENU = NULL;
47 char *ABOUT_MENU = NULL;
48 char *BACK_MENU = NULL;
49 char *SETTINGS_SND_MENU = NULL;
50 char *SETTINGS_GFX_MENU = NULL;
51 char *SETTINGS_OTH_MENU = NULL;
52 char *CUSTOM_THEME_MENU = NULL;
53 char *OWN_THEME_MENU = NULL;
54 char *WAIT_MENU = NULL;
55 char *SELECT_GAME_MENU = NULL;
56 char *SELECT_THEME_MENU = NULL;
57 char *SAVED_MENU = NULL;
58 char *NOGAMES_MENU = NULL;
59 char *NOTHEMES_MENU = NULL;
60 char *BROWSE_MENU = NULL;
61 char *QUIT_MENU = NULL;
62 char *REMOVE_MENU = NULL;
63 char *ON = NULL;
64 char *OFF = NULL;
65 
66 char *KBD_MODE_LINKS = NULL;
67 char *KBD_MODE_SMART = NULL;
68 char *KBD_MODE_SCROLL = NULL;
69 char *CANCEL_MENU = NULL;
70 
71 char *FROM_THEME = NULL;
72 
73 char *DISABLED_SAVE_MENU = NULL;
74 
75 static char  menu_buff[8192];
76 
slot_name(const char * path)77 static char *slot_name(const char *path)
78 {
79 	struct stat 	st;
80 	char *l;
81 	if (stat(path, &st))
82 		return NULL;
83 	l = lookup_tag(path, "Name", "--");
84 	if (l) {
85 		trunc_lines(l, 0);
86 		if (!is_empty(l) && game_tag_valid(l)) {
87 			int y;
88 			char *m;
89 			static char *months[] = {/* to work on every locale ;)*/
90 				"Jan", "Feb",
91 				"Mar", "Apr", "May",
92 				"Jun", "Jul", "Aug",
93 				"Sep", "Oct", "Nov",
94 				"Dec",
95 			};
96 			struct tm *tm;
97 			time_t t;
98 
99 			char *s = instead_fromgame(l);
100 			free(l);
101 			if (!s)
102 				return s;
103 			time(&t);
104 			tm = localtime(&t);
105 			y = tm->tm_year;
106 			tm = localtime(&st.st_mtime);
107 			l = malloc(strlen(s) + 64);
108 			if (!l)
109 				return s;
110 			if (tm->tm_mon >=0 && tm->tm_mon < 12)
111 				m = months[tm->tm_mon];
112 			else
113 				m = "?";
114 			if (tm->tm_year == y)
115 				snprintf(l, 64, "%02d %s %02d:%02d - ",
116 					tm->tm_mday, m, tm->tm_hour, tm->tm_min);
117 			else {
118 				if (tm->tm_year < 1900)
119 					tm->tm_year += 1900;
120 				snprintf(l, 64, "%02d %s %02d:%02d %04d - ",
121 					tm->tm_mday, m, tm->tm_hour, tm->tm_min, tm->tm_year);
122 			}
123 			strcat(l, s);
124 			free(s);
125 			return l;
126 		}
127 		free(l);
128 	}
129 	l = ctime(&st.st_mtime);
130 	if (!l)
131 		return NULL;
132 	l[strcspn(l,"\n")] = 0;
133 	return strdup(l);
134 }
135 
load_menu(void)136 static void load_menu(void)
137 {
138 	int i;
139 	*menu_buff = 0;
140 	/*
141 	if (!game_saves_enabled()) {
142 		strcat(menu_buff, DISABLED_SAVE_MENU);
143 		strcat(menu_buff, CANCEL_MENU);
144 		return;
145 	} */
146 	strcpy(menu_buff, SELECT_LOAD_MENU);
147 	for (i = 0; i < MAX_SAVE_SLOTS; i ++) {
148 		char tmp[PATH_MAX];
149 		char *s = game_save_path(0, i);
150 		if (!s || access(s, R_OK)) {
151 			if (!i)
152 				continue;
153 			snprintf(tmp, sizeof(tmp), "<l>%d - %s\n</l>", i, SAVE_SLOT_EMPTY);
154 		} else {
155 			char *name;
156 			if (!i)
157 				name = strdup(AUTOSAVE_SLOT);
158 			else
159 				name = slot_name(s);
160 			if (!name)
161 				snprintf(tmp, sizeof(tmp), "<l>%d - %s</l>\n", i, BROKEN_SLOT);
162 			else {
163 				snprintf(tmp, sizeof(tmp), "<l>%d - <a:/load%d>%s</a></l>\n", i, i, name);
164 				free(name);
165 			}
166 		}
167 		strcat(menu_buff, tmp);
168 	}
169 	strcat(menu_buff,"\n");
170 	strcat(menu_buff, CANCEL_MENU);
171 }
172 
save_menu(void)173 static void save_menu(void)
174 {
175 	int i;
176 	*menu_buff = 0;
177 	if (!game_saves_enabled()) {
178 		strcat(menu_buff, DISABLED_SAVE_MENU);
179 		strcat(menu_buff, CANCEL_MENU);
180 		return;
181 	}
182 	strcpy(menu_buff, SELECT_SAVE_MENU);
183 	for (i = 1; i < MAX_SAVE_SLOTS; i ++) {
184 		char tmp[PATH_MAX];
185 		char *s = game_save_path(0, i);
186 		if (!s || access(s, R_OK))
187 			snprintf(tmp, sizeof(tmp), "<l>%d - <a:/save%d>%s</a></l>\n", i, i, SAVE_SLOT_EMPTY);
188 		else {
189 			char *name;
190 			if (!i)
191 				name = strdup(AUTOSAVE_SLOT);
192 			else
193 				name = slot_name(s);
194 			if (!name)
195 				snprintf(tmp, sizeof(tmp), "<l>%d - <a:/save%d>%s</a></l>\n", i, i, BROKEN_SLOT);
196 			else {
197 				snprintf(tmp, sizeof(tmp), "<l>%d - <a:/save%d>%s</a></l>\n", i, i, name);
198 				free(name);
199 			}
200 		}
201 		strcat(menu_buff, tmp);
202 	}
203 	strcat(menu_buff,"\n");
204 	strcat(menu_buff, CANCEL_MENU);
205 }
206 
pages_menu(char * res,int nr,int max,const char * menu,const char * append)207 static int pages_menu(char *res, int nr, int max, const char *menu, const char *append)
208 {
209 	static char buff[256];
210 	int k = MENU_PER_PAGER;
211 	int i = nr - MENU_PER_PAGER / 2;
212 
213 	if (i < 0)
214 		i = 0;
215 	else if (max - i < MENU_PER_PAGER)
216 		i = max - MENU_PER_PAGER;
217 	if (i < 0)
218 		i = 0;
219 	if (nr)
220 		sprintf(buff, "<a:/%s prev><<</a> ", menu);
221 	else
222 		sprintf(buff, "<< ");
223 	strcat(res, buff);
224 	for (; i < max && k-- ; i ++) {
225 		if (i != nr)
226 			sprintf(buff, "<a:/%s %d>[%d]</a> ", menu, i, i + 1);
227 		else
228 			sprintf(buff, "[%d] ", i + 1);
229 		strcat(res, buff);
230 	}
231 	if ((nr + 1) != max)
232 		sprintf(buff, "<a:/%s next>>></a>", menu);
233 	else
234 		sprintf(buff, ">>");
235 	strcat(res, buff);
236 	strcat(res, append);
237 	return 0;
238 }
239 
games_menu(void)240 static void games_menu(void)
241 {
242 	int i, n;
243 #ifdef _USE_BROWSE
244 	int append_browse = 0;
245 #endif
246 	char tmp[PATH_MAX];
247 #ifdef _USE_BROWSE
248 	snprintf(tmp, sizeof(tmp), " <u><a:/browse>%s</a></u>\n", BROWSE_MENU);
249 #else
250 	snprintf(tmp, sizeof(tmp), "\n");
251 #endif
252 	strcpy(menu_buff, SELECT_GAME_MENU);
253 	if ((games_nr - 1) / MENU_GAMES_MAX)
254 		pages_menu(menu_buff, games_menu_from / MENU_GAMES_MAX, (games_nr - 1) / MENU_GAMES_MAX + 1, "games", tmp);
255 #ifdef _USE_BROWSE
256 	else
257 		append_browse = 1;
258 #endif
259 	for (i = games_menu_from, n = 0; i < games_nr && n < MENU_GAMES_MAX; i ++) {
260 
261 		if (!games[i].name[0]) /* empty */
262 			continue;
263 		if (curgame_dir && !strcmp(games[i].dir, curgame_dir))
264 			snprintf(tmp, sizeof(tmp), "<l><a:/resume><b>%s</b></a></l>", games[i].name);
265 		else
266 			snprintf(tmp, sizeof(tmp), "<l><a:%s>%s</a></l>", games[i].dir, games[i].name);
267 		if (!strncmp(game_local_games_path(0), games[i].path, strlen(game_local_games_path(0))) &&
268 			!access(games[i].path, W_OK)) {
269 			snprintf(tmp + strlen(tmp), sizeof(tmp), " [<a:/remove_%d>X</a>]\n", i);
270 		} else
271 			strcat(tmp, "\n");
272 
273 		strcat(menu_buff, tmp);
274 		n ++;
275 	}
276 	for(;n < MENU_GAMES_MAX && games_nr > MENU_GAMES_MAX; n++) /* align h */
277 		strcat(menu_buff, "\n");
278 #ifdef _USE_BROWSE
279 	if (append_browse) {
280 		snprintf(tmp, sizeof(tmp), "<u><a:/browse>%s</a></u>\n", BROWSE_MENU);
281 		strcat(menu_buff, tmp);
282 	}
283 #endif
284 	if (!games_nr) {
285 		sprintf(menu_buff, NOGAMES_MENU, GAMES_PATH);
286 #ifdef _USE_BROWSE
287 		strcat(menu_buff,"\n");
288 		snprintf(tmp, sizeof(tmp), "<u><a:/browse>%s</a></u>\n", BROWSE_MENU);
289 		strcat(menu_buff, tmp);
290 #endif
291 	}
292 	strcat(menu_buff,"\n");
293 	strcat(menu_buff, BACK_MENU);
294 }
295 
games_menu_maxw(void)296 int games_menu_maxw(void)
297 {
298 	int i = 0;
299 	int oldm = games_menu_from;
300 	int maxw = 0;
301 	for (i = 0; i < games_nr; i += MENU_GAMES_MAX) {
302 		int w;
303 		games_menu_from = i;
304 		games_menu();
305 		game_menu_box_wh(menu_buff, &w, NULL);
306 		if (w > maxw)
307 			maxw = w;
308 	}
309 	games_menu_from = oldm;
310 	return maxw;
311 }
312 
themes_menu(void)313 static void themes_menu(void)
314 {
315 	int i, n, m;
316 	int type;
317 	int count = themes_max(&type);
318 	int pages = 0;
319 
320 	if (count > 0)
321 		pages = (count - 1) / MENU_THEMES_MAX + 1;
322 
323 	if (themes_menu_page >= pages)
324 		themes_menu_page = 0;
325 
326 	strcpy(menu_buff, SELECT_THEME_MENU);
327 
328 	if (pages > 1)
329 		pages_menu(menu_buff, themes_menu_page, pages, "themes", "\n");
330 
331 	for (i = 0, n = 0, m = 0; i < themes_nr && n < MENU_THEMES_MAX; i ++) {
332 		char tmp[PATH_MAX];
333 		if (!themes[i].name[0]) /* empty */
334 			continue;
335 		if (themes[i].type != type)
336 			continue;
337 		m ++;
338 		if ((m - 1) / MENU_THEMES_MAX < themes_menu_page)
339 			continue;
340 		if (curtheme_dir[type] && !strcmp(themes[i].dir, curtheme_dir[type]))
341 			snprintf(tmp, sizeof(tmp), "<l><a:/resume><b>%s</b></a></l>\n", themes[i].name);
342 		else
343 			snprintf(tmp, sizeof(tmp), "<l><a:%s>%s</a></l>\n", themes[i].dir, themes[i].name);
344 		strcat(menu_buff, tmp);
345 		n ++;
346 	}
347 
348 	for(;n < MENU_THEMES_MAX && count > MENU_THEMES_MAX; n++) /* align h */
349 		strcat(menu_buff, "\n");
350 
351 	if (!count)
352 		sprintf(menu_buff, NOTHEMES_MENU, THEMES_PATH);
353 	strcat(menu_buff, "\n");
354 	strcat(menu_buff, BACK_MENU);
355 }
356 
themes_menu_maxw(void)357 int themes_menu_maxw(void)
358 {
359 	int i = 0;
360 	int oldm = themes_menu_page;
361 	int maxw = 0;
362 	int pages = 0;
363 	int count = themes_max(NULL);
364 	if (count > 0)
365 		pages = (count - 1) / MENU_THEMES_MAX + 1;
366 	else
367 		pages = 1;
368 	for (i = 0; i < pages; i ++) {
369 		int w;
370 		themes_menu_page = i;
371 		themes_menu();
372 		game_menu_box_wh(menu_buff, &w, NULL);
373 		if (w > maxw)
374 			maxw = w;
375 	}
376 	themes_menu_page = oldm;
377 	return maxw;
378 }
379 
opt_get_mode(void)380 static char *opt_get_mode(void)
381 {
382 	static char buff[128];
383 	if (opt_mode[0] == -1 || opt_mode[1] == -1) {
384 		snprintf(buff, sizeof(buff), "%s", FROM_THEME);
385 		return buff;
386 	}
387 	snprintf(buff, sizeof(buff), "%dx%d", opt_mode[0], opt_mode[1]);
388 	return buff;
389 }
390 
391 static int gtr = 0;
392 static int menu_settings_num = 0;
393 
menu_strip_tag(const char * a,const char * b)394 static void menu_strip_tag(const char *a, const char *b)
395 {
396 	char *p, *ep;
397 	size_t len;
398 	p = strstr(menu_buff, a);
399 	if (!p)
400 		return;
401 	ep = strstr(p, b);
402 	if (!ep)
403 		return;
404 /*	ep += strcspn(ep, "\n\r");
405 	ep += strspn(ep, "\n\r"); */
406 	ep += strlen(b);
407 	len = strlen(ep);
408 	memmove(p, ep, len);
409 	p[len] = 0;
410 }
411 
menu_remove_tag(const char * a,const char * b)412 static void menu_remove_tag(const char *a, const char *b)
413 {
414 	char *p, *ep;
415 	size_t len;
416 	p = strstr(menu_buff, a);
417 	if (!p)
418 		return;
419 	ep = p + strlen(a);
420 	len = strlen(ep);
421 	memmove(p, ep, len);
422 	p[len] = 0;
423 
424 	ep = strstr(p, b);
425 	if (!ep)
426 		return;
427 	p = ep;
428 	ep += strlen(b);
429 	len = strlen(ep);
430 
431 	memmove(p, ep, len);
432 	p[len] = 0;
433 }
434 
game_menu_gen(void)435 char *game_menu_gen(void)
436 {
437 	if (cur_menu == menu_main) {
438 		strcpy(menu_buff, MAIN_MENU);
439 		if (standalone_sw) {
440 			int count = 0, n;
441 			menu_strip_tag("<?:select>", "</?>");
442 			count = themes_count(THEME_GLOBAL);
443 			if (curgame_dir && opt_owntheme) {
444 				if ((n = themes_count(THEME_GAME)) > 0)
445 					count = n;
446 				else if (game_own_theme)
447 					count = 1;
448 			}
449 			if (count <= 1)
450 				menu_strip_tag("<?:themes>", "</?>");
451 			else
452 				menu_remove_tag("<?:themes>", "</?>");
453 		} else {
454 			menu_remove_tag("<?:select>", "</?>");
455 			menu_remove_tag("<?:themes>", "</?>");
456 		}
457 	} else if (cur_menu == menu_about || cur_menu == menu_about_instead) {
458 		struct game *g;
459 		if (cur_menu == menu_about && curgame_dir && (g = game_lookup(curgame_dir))) {
460 			char version[32];
461 			char author[64];
462 			char info[192];
463 			char instead[64];
464 
465 			if (g->version)
466 				snprintf(version, sizeof(version), "%s", g->version);
467 			else
468 				strcpy(version, "1.0");
469 
470 			if (g->author)
471 				snprintf(author, sizeof(author), "\n%s", g->author);
472 			else
473 				strcpy(author, "");
474 
475 			if (g->info)
476 				snprintf(info, sizeof(info), "\n\n%s", g->info);
477 			else
478 				strcpy(info, "");
479 
480 			if (!standalone_sw)
481 				snprintf(instead, sizeof(instead), "<a:/about-instead>INSTEAD</a> | ");
482 			else
483 				strcpy(instead, "");
484 			author[sizeof(author) - 1] = 0;
485 			version[sizeof(version) - 1] = 0;
486 			info[sizeof(info) - 1] = 0;
487 			snprintf(menu_buff, sizeof(menu_buff), "%s - %s%s%s\n\n%s%s",
488 				g->name, version,
489 				author, info,
490 				instead, BACK_MENU);
491 		} else {
492 			snprintf(menu_buff, sizeof(menu_buff), ABOUT_MENU, VERSION);
493 		}
494 	} else if (cur_menu == menu_settings) {
495 		char *just[JUST_MAX] = { FROM_THEME, OFF, ON };
496 		char *kbd [KBD_MAX] = { KBD_MODE_SMART, KBD_MODE_LINKS, KBD_MODE_SCROLL };
497 		int fsize = 100 + (10 * opt_fsize);
498 		opt_kbd = (unsigned int)opt_kbd % KBD_MAX;
499 		opt_justify = (unsigned int)opt_justify % JUST_MAX;
500 		switch (menu_settings_num) {
501 		case 0:
502 			snprintf(menu_buff, sizeof(menu_buff), SETTINGS_GFX_MENU,
503 			opt_get_mode(), opt_fs?ON:OFF, opt_hires?ON:OFF, fsize, just[opt_justify],
504 				opt_hl?ON:OFF, opt_fading?ON:OFF, opt_owntheme?ON:OFF);
505 			if (standalone_sw)
506 				menu_strip_tag("<?:owntheme>", "</?>");
507 			else
508 				menu_remove_tag("<?:owntheme>", "</?>");
509 			break;
510 		case 1:
511 			snprintf(menu_buff, sizeof(menu_buff), SETTINGS_SND_MENU,
512 			snd_vol_to_pcn(snd_volume_mus(-1)), snd_hz(), opt_music?ON:OFF, opt_click?ON:OFF);
513 			break;
514 		case 2:
515 			snprintf(menu_buff, sizeof(menu_buff), SETTINGS_OTH_MENU,
516 			opt_motion?ON:OFF, opt_filter?ON:OFF, kbd[opt_kbd],
517 			langs[cur_lang].name,  (opt_autosave & 1)?ON:OFF);
518 			break;
519 		}
520 	} else if (cur_menu == menu_askquit) {
521 		strcpy(menu_buff, QUIT_MENU);
522 	} else if (cur_menu == menu_saved) {
523 		strcpy(menu_buff, SAVED_MENU);
524 	} else if (cur_menu == menu_games) {
525 		games_menu();
526 	} else if (cur_menu == menu_themes) {
527 		themes_menu();
528 	} else if (cur_menu == menu_own_theme) {
529 		strcpy(menu_buff, OWN_THEME_MENU);
530 	} else if (cur_menu == menu_wait) {
531 		strcpy(menu_buff, WAIT_MENU);
532 	} else if (cur_menu == menu_custom_theme) {
533 		strcpy(menu_buff, CUSTOM_THEME_MENU);
534 	} else if (cur_menu == menu_load) {
535 		load_menu();
536 	} else if (cur_menu == menu_save) {
537 		save_menu();
538 	} else if (cur_menu == menu_error) {
539 		snprintf(menu_buff, sizeof(menu_buff),
540 		ERROR_MENU, instead_err()?instead_err():UNKNOWN_ERROR);
541 		instead_err_msg(NULL);
542 	} else if (cur_menu == menu_warning) {
543 		snprintf(menu_buff, sizeof(menu_buff),
544 		WARNING_MENU, instead_err()?instead_err():UNKNOWN_ERROR);
545 		instead_err_msg(NULL);
546 	} else if (cur_menu == menu_remove) {
547 		const char *sname = games[gtr].path;
548 		if (strlen(games[gtr].path) >= 48) {
549 			sname = games[gtr].name;
550 		}
551 		snprintf(menu_buff, sizeof(menu_buff), REMOVE_MENU, sname);
552 	}
553 	return menu_buff;
554 }
555 
game_menu_act(const char * a)556 int game_menu_act(const char *a)
557 {
558 	static int old_vol = 0;
559 
560 	if (!strcmp(a, "/autosave")) {
561 		opt_autosave = !(opt_autosave & 1);
562 		game_menu_box(1, game_menu_gen());
563 	} else if (!strcmp(a, "/kbd")) {
564 		opt_kbd += 1;
565 		if (opt_kbd == KBD_MAX)
566 			opt_kbd = 0;
567 		game_menu_box(1, game_menu_gen());
568 	} else if (!strcmp(a, "/owntheme")) {
569 		opt_owntheme = !opt_owntheme;
570 		if (game_own_theme)
571 			restart_needed = 1;
572 		game_menu_box(1, game_menu_gen());
573 	} else if (!strcmp(a, "/motion")) {
574 		opt_motion ^= 1;
575 		game_menu_box(1, game_menu_gen());
576 	} else if (!strcmp(a, "/filter")) {
577 		opt_filter ^= 1;
578 		game_menu_box(1, game_menu_gen());
579 	} else if (!strcmp(a, "/click")) {
580 		opt_click ^= 1;
581 		game_menu_box(1, game_menu_gen());
582 	} else if (!strcmp(a, "/mode++")) {
583 		if (gfx_next_mode(&opt_mode[0], &opt_mode[1]))
584 			opt_mode[0] = opt_mode[1] = -1;
585 		restart_needed = 1;
586 		game_menu_box(1, game_menu_gen());
587 	} else if (!strcmp(a, "/mode--")) {
588 		if (gfx_prev_mode(&opt_mode[0], &opt_mode[1]))
589 			opt_mode[0] = opt_mode[1] = -1;
590 		restart_needed = 1;
591 		game_menu_box(1, game_menu_gen());
592 	} else if (!strcmp(a, "/just++")) {
593 		restart_needed = 1;
594 		opt_justify ++;
595 		if (opt_justify >= JUST_MAX)
596 			opt_justify = 0;
597 		game_menu_box(1, game_menu_gen());
598 	} else if (!strcmp(a, "/just--")) {
599 		restart_needed = 1;
600 		opt_justify --;
601 		if (opt_justify < 0)
602 			opt_justify = JUST_MAX - 1;
603 		game_menu_box(1, game_menu_gen());
604 	} else if (!strcmp(a, "/fs--")) {
605 		opt_fsize --;
606 		if (opt_fsize >= FONT_MIN_SZ) {
607 			restart_needed = 1;
608 		} else
609 			opt_fsize ++;
610 		game_menu_box(1, game_menu_gen());
611 	} else if (!strcmp(a, "/fs++")) {
612 		opt_fsize ++;
613 		if (opt_fsize <= FONT_MAX_SZ) {
614 			restart_needed = 1;
615 		} else
616 			opt_fsize --;
617 		game_menu_box(1, game_menu_gen());
618 	} else if (!strcmp(a, "/hl")) {
619 		opt_hl ^= 1;
620 		game_menu_box(1, game_menu_gen());
621 	} else if (!strcmp(a, "/fading")) {
622 		opt_fading ^= 1;
623 		game_menu_box(1, game_menu_gen());
624 	} else if (!strcmp(a, "/fs")) {
625 #if !defined(ANDROID) && !defined(IOS)
626 		restart_needed = 1;
627 		opt_fs ^= 1;
628 		game_menu_box(1, game_menu_gen());
629 #endif
630 	} else if (!strcmp(a, "/hires")) {
631 		opt_hires ^= 1;
632 		restart_needed = 1;
633 		game_menu_box(1, game_menu_gen());
634 	} else if (!strncmp(a, "/games ", 7)) {
635 		if (!strcmp(a + 7, "prev")) {
636 			games_menu_from -= MENU_GAMES_MAX;
637 			if (games_menu_from < 0)
638 				games_menu_from = 0;
639 		} else if (!strcmp(a + 7, "next")) {
640 			if (games_menu_from + MENU_GAMES_MAX < games_nr)
641 				games_menu_from += MENU_GAMES_MAX;
642 		} else {
643 			int nr = atoi(a + 7);
644 			games_menu_from = nr * MENU_GAMES_MAX;
645 		}
646 		game_menu_box(1, game_menu_gen());
647 	} else if (!strncmp(a, "/themes ", 8)) {
648 		if (!strcmp(a + 8, "prev")) {
649 			themes_menu_page --;
650 			if (themes_menu_page < 0)
651 				themes_menu_page = 0;
652 		} else if (!strcmp(a + 8, "next")) {
653 			int count = themes_max(NULL); int pages = 0;
654 			if (count > 0)
655 				pages = (count - 1) / MENU_THEMES_MAX + 1;
656 			if (themes_menu_page + 1 < pages)
657 				themes_menu_page ++;
658 		} else {
659 			int nr = atoi(a + 8);
660 			themes_menu_page = nr;
661 		}
662 		game_menu_box(1, game_menu_gen());
663 	} else if (!strcmp(a, "/select")) {
664 		game_menu(menu_games);
665 	} else if (!strcmp(a, "/remove")) {
666 		fprintf(stderr,"Removing '%s'\n", games[gtr].path);
667 		if (curgame_dir && !strcmp(curgame_dir, games[gtr].dir)) {
668 			game_done(0);
669 			if (game_init(NULL)) {
670 				game_error();
671 				return 0;
672 			}
673 		}
674 		games_remove(gtr);
675 		if (games_menu_from >= games_nr)
676 			games_menu_from -= MENU_GAMES_MAX;
677 		if (games_menu_from < 0)
678 			games_menu_from = 0;
679 		game_menu(menu_games);
680 	} else if (!strcmp(a, "/themes")) {
681 		game_menu(menu_themes);
682 	} else if (!strcmp(a, "/save_menu")) {
683 		if (curgame_dir)
684 			game_menu(menu_save);
685 	} else if (!strncmp(a, "/save", 5)) {
686 		if (!game_save(atoi(a + 5))) {
687 			game_menu(menu_saved);
688 		}
689 	} else if (!strcmp(a, "/load_menu")) {
690 		if (curgame_dir)
691 			game_menu(menu_load);
692 	} else if (!strncmp(a, "/load", 5)) {
693 		int nr = atoi(a + 5);
694 		if (!curgame_dir)
695 			return 0;
696 		game_menu_box(0, NULL);
697 		if (!game_reset()) {
698 			game_load(nr);
699 			cur_menu = menu_main;
700 		}
701 /*		game_menu_box(0, NULL); */
702 	} else if (!strcmp(a, "/new")) {
703 		char *s;
704 		if (!curgame_dir)
705 			return 0;
706 
707 /* remove autlosave */
708 		s = game_save_path(0, 0);
709 		if (s && !access(s, R_OK) && (opt_autosave & 1))
710 			unlink (s);
711 		game_menu_box(0, NULL);
712 		if (!game_reset()) {
713 			game_cmd("look", 0);
714 			custom_theme_warn();
715 		}
716 	} else if (!strcmp(a,"/main")) {
717 		if (top_menu != menu_main) {
718 			cur_menu = menu_main;
719 			game_menu_box(0, NULL);
720 		} else
721 			game_menu(menu_main);
722 		if (restart_needed) {
723 			game_restart();
724 			restart_needed = 0;
725 		}
726 	} else if (!strcmp(a,"/ask_quit")) {
727 		game_menu(menu_askquit);
728 	} else if (!strncmp(a, "/remove_", 8)) {
729 		gtr = atoi(a + 8);
730 		game_menu(menu_remove);
731 	} else if (!strcmp(a,"/about")) {
732 		game_menu(menu_about);
733 	} else if (!strcmp(a,"/about-instead")) {
734 		game_menu(menu_about_instead);
735 	} else if (!strcmp(a,"/mtoggle")) {
736 		if (!old_vol) {
737 			old_vol = snd_volume_mus(-1);
738 			game_change_vol(0, 0);
739 		} else {
740 			game_change_vol(0, old_vol);
741 			old_vol = 0;
742 		}
743 		game_menu_box(1, game_menu_gen());
744 	} else if (!strcmp(a,"/music")) {
745 		opt_music ^= 1;
746 		if (!opt_music) {
747 			game_stop_mus(0);
748 		} else
749 			game_music_player();
750 		game_menu_box(1, game_menu_gen());
751 	} else if (!strcmp(a,"/resume")) {
752 		cur_menu = menu_main;
753 		game_menu_box(0, NULL);
754 	} else if (!strcmp(a, "/settings")) {
755 		game_menu(menu_settings);
756 	} else if (!strcmp(a, "/settings-gfx")) {
757 		menu_settings_num = 0;
758 		game_menu_box(1, game_menu_gen());
759 	} else if (!strcmp(a, "/settings-snd")) {
760 		menu_settings_num = 1;
761 		game_menu_box(1, game_menu_gen());
762 	} else if (!strcmp(a, "/settings-oth")) {
763 		menu_settings_num = 2;
764 		game_menu_box(1, game_menu_gen());
765 	} else if (!strcmp(a, "/vol--")) {
766 		game_change_vol(-10, 0);
767 		game_menu_box(1, game_menu_gen());
768 	} else if (!strcmp(a, "/vol++")) {
769 		game_change_vol(+10, 0);
770 		game_menu_box(1, game_menu_gen());
771 	} else if (!strcmp(a, "/vol-")) {
772 		game_change_vol(-1, 0);
773 		game_menu_box(1, game_menu_gen());
774 	} else if (!strcmp(a, "/vol+")) {
775 		game_change_vol(+1, 0);
776 		game_menu_box(1, game_menu_gen());
777 	} else if (!strcmp(a, "/hz-")) {
778 		int hz = snd_hz();
779 		if (hz == 44100)
780 			hz = 22050;
781 		else if (hz == 22050)
782 			hz = 11025;
783 		else if (hz == 11025)
784 			hz = 0;
785 		else
786 			hz = 22050;
787 		game_change_hz(hz);
788 		game_menu_box(1, game_menu_gen());
789 	} else if (!strcmp(a, "/hz+")) {
790 		int hz = snd_hz();
791 		if (hz == 11025)
792 			hz = 22050;
793 		else if (hz == 22050)
794 			hz = 44100;
795 		else if (hz == 44100)
796 			hz = 0;
797 		else
798 			hz = 22050;
799 		game_change_hz(hz);
800 		game_menu_box(1, game_menu_gen());
801 	} else if (!strcmp(a, "/lang++")) {
802 		do {
803 			cur_lang ++;
804 			if (cur_lang >= langs_nr)
805 				cur_lang = 0;
806 		} while (menu_lang_select(langs[cur_lang].file));
807 		if (curgame_dir)
808 			instead_set_lang(opt_lang);
809 		themes_rename();
810 		games_rename();
811 		game_reset_name();
812 		game_menu_box(1, game_menu_gen());
813 	} else if (!strcmp(a, "/lang--")) {
814 		do {
815 			cur_lang --;
816 			if (cur_lang < 0)
817 			cur_lang = langs_nr - 1;
818 		} while (menu_lang_select(langs[cur_lang].file));
819 		if (curgame_dir)
820 			instead_set_lang(opt_lang);
821 		themes_rename();
822 		games_rename();
823 		game_reset_name();
824 		game_menu_box(1, game_menu_gen());
825 	} else if (!strcmp(a,"/quit")) {
826 		return -1;
827 #ifdef _USE_BROWSE
828 	} else if (!strcmp(a,"/browse")) {
829 		game_from_disk();
830 		return 0;
831 #endif
832 	} else if (cur_menu == menu_games) {
833 		char *p;
834 		p = strdup(a);
835 		if (p) {
836 			game_done(0);
837 			if (game_init(p)) {
838 				game_error();
839 			}
840 			free(p);
841 		}
842 	} else if (cur_menu == menu_themes) {
843 		char *p;
844 		p = strdup(a);
845 		if (p) {
846 			char *og;
847 			if (game_theme_select(p))
848 				fprintf(stderr, "Can't select theme:%s:%s\n", p, strerror(errno));
849 			og = curgame_dir;
850 			game_save(-1);
851 			game_done(0);
852 			if (game_init(og)) {
853 				game_error();
854 			} else if (curgame_dir && game_own_theme && opt_owntheme && !curtheme_dir[THEME_GAME]) {
855 				game_menu(menu_own_theme);
856 			}
857 			free(p);
858 		}
859 	}
860 	return 0;
861 }
862 
custom_theme_warn(void)863 void custom_theme_warn(void)
864 {
865 	if (game_own_theme && !opt_owntheme && cur_menu != menu_warning) {
866 		game_menu(menu_custom_theme);
867 	}
868 }
869 
870 
871 
872 struct	lang *langs = NULL;
873 int	langs_nr = 0;
874 
875 
lang_free(void)876 static void lang_free(void)
877 {
878 	FREE(UNKNOWN_ERROR);
879 	FREE(ERROR_MENU);
880 	FREE(WARNING_MENU);
881 	FREE(SAVE_SLOT_EMPTY);
882 	FREE(SELECT_LOAD_MENU);
883 	FREE(AUTOSAVE_SLOT);
884 	FREE(BROKEN_SLOT);
885 	FREE(SELECT_SAVE_MENU);
886 	FREE(MAIN_MENU);
887 	FREE(ABOUT_MENU);
888 	FREE(BACK_MENU);
889 	FREE(SETTINGS_SND_MENU);
890 	FREE(SETTINGS_GFX_MENU);
891 	FREE(SETTINGS_OTH_MENU);
892 	FREE(CUSTOM_THEME_MENU);
893 	FREE(OWN_THEME_MENU);
894 	FREE(WAIT_MENU);
895 	FREE(SELECT_GAME_MENU);
896 	FREE(SELECT_THEME_MENU);
897 	FREE(SAVED_MENU);
898 	FREE(NOGAMES_MENU);
899 	FREE(NOTHEMES_MENU);
900 	FREE(QUIT_MENU);
901 	FREE(REMOVE_MENU);
902 	FREE(ON);
903 	FREE(OFF);
904 	FREE(KBD_MODE_LINKS);
905 	FREE(KBD_MODE_SMART);
906 	FREE(KBD_MODE_SCROLL);
907 	FREE(CANCEL_MENU);
908 	FREE(FROM_THEME);
909 	FREE(DISABLED_SAVE_MENU);
910 	FREE(BROWSE_MENU);
911 }
912 
lang_ok(void)913 static int lang_ok(void)
914 {
915 	if (UNKNOWN_ERROR && ERROR_MENU && WARNING_MENU && SAVE_SLOT_EMPTY &&
916 		SELECT_LOAD_MENU && AUTOSAVE_SLOT && BROKEN_SLOT && SELECT_SAVE_MENU &&
917 		MAIN_MENU && ABOUT_MENU && BACK_MENU && SETTINGS_SND_MENU && SETTINGS_GFX_MENU && SETTINGS_OTH_MENU &&
918 		CUSTOM_THEME_MENU && OWN_THEME_MENU && SELECT_GAME_MENU && SELECT_THEME_MENU && WAIT_MENU &&
919 		SAVED_MENU && NOGAMES_MENU && NOTHEMES_MENU && QUIT_MENU && REMOVE_MENU &&
920 		ON && OFF && KBD_MODE_LINKS && KBD_MODE_SMART && KBD_MODE_SCROLL && CANCEL_MENU &&
921 		FROM_THEME && DISABLED_SAVE_MENU && BROWSE_MENU)
922 		return 0;
923 	return -1;
924 }
925 
926 struct parser lang_parser[] = {
927 	{ "UNKNOWN_ERROR", parse_esc_string, &UNKNOWN_ERROR, 0 },
928 	{ "ERROR_MENU", parse_esc_string, &ERROR_MENU, 0 },
929 	{ "WARNING_MENU", parse_esc_string, &WARNING_MENU, 0 },
930 	{ "SAVE_SLOT_EMPTY", parse_esc_string, &SAVE_SLOT_EMPTY, 0 },
931 	{ "SELECT_LOAD_MENU", parse_esc_string, &SELECT_LOAD_MENU, 0 },
932 	{ "AUTOSAVE_SLOT", parse_esc_string, &AUTOSAVE_SLOT, 0 },
933 	{ "BROKEN_SLOT", parse_esc_string, &BROKEN_SLOT, 0 },
934 	{ "SELECT_SAVE_MENU", parse_esc_string, &SELECT_SAVE_MENU, 0 },
935 	{ "MAIN_MENU", parse_esc_string, &MAIN_MENU, 0 },
936 	{ "ABOUT_MENU", parse_esc_string, &ABOUT_MENU, 0 },
937 	{ "BACK_MENU", parse_esc_string, &BACK_MENU, 0 },
938 	{ "SETTINGS_GFX_MENU", parse_esc_string, &SETTINGS_GFX_MENU, 0 },
939 	{ "SETTINGS_SND_MENU", parse_esc_string, &SETTINGS_SND_MENU, 0 },
940 	{ "SETTINGS_OTH_MENU", parse_esc_string, &SETTINGS_OTH_MENU, 0 },
941 	{ "CUSTOM_THEME_MENU", parse_esc_string, &CUSTOM_THEME_MENU, 0 },
942 	{ "OWN_THEME_MENU", parse_esc_string, &OWN_THEME_MENU, 0 },
943 	{ "WAIT_MENU", parse_esc_string, &WAIT_MENU, 0 },
944 	{ "SELECT_GAME_MENU", parse_esc_string, &SELECT_GAME_MENU, 0 },
945 	{ "SELECT_THEME_MENU", parse_esc_string, &SELECT_THEME_MENU, 0 },
946 	{ "SAVED_MENU", parse_esc_string, &SAVED_MENU, 0 },
947 	{ "NOGAMES_MENU", parse_esc_string, &NOGAMES_MENU, 0 },
948 	{ "NOTHEMES_MENU", parse_esc_string, &NOTHEMES_MENU, 0 },
949 	{ "QUIT_MENU", parse_esc_string, &QUIT_MENU, 0 },
950 	{ "REMOVE_MENU", parse_esc_string, &REMOVE_MENU, 0 },
951 	{ "ON", parse_esc_string, &ON, 0 },
952 	{ "OFF", parse_esc_string, &OFF, 0 },
953 	{ "KBD_MODE_LINKS", parse_esc_string, &KBD_MODE_LINKS, 0 },
954 	{ "KBD_MODE_SMART", parse_esc_string, &KBD_MODE_SMART, 0 },
955 	{ "KBD_MODE_SCROLL", parse_esc_string, &KBD_MODE_SCROLL, 0 },
956 	{ "CANCEL_MENU", parse_esc_string, &CANCEL_MENU, 0 },
957 	{ "FROM_THEME", parse_esc_string, &FROM_THEME, 0 },
958 	{ "DISABLED_SAVE_MENU", parse_esc_string, &DISABLED_SAVE_MENU, 0 },
959 	{ "BROWSE_MENU", parse_esc_string, &BROWSE_MENU, 0 },
960 	{ NULL,  NULL, NULL, 0 },
961 };
962 
lang_parse(const char * path)963 static int lang_parse(const char *path)
964 {
965 	return parse_ini(path, lang_parser);
966 }
967 
is_lang(const char * path,const char * n)968 static int is_lang(const char *path, const char *n)
969 {
970 	char *p = getfilepath(path, n);
971 	if (!p)
972 		return 0;
973 	if (access(p, F_OK))
974 		return 0;
975 	free(p);
976 	if (!(p = strstr(n, ".ini")) && !(p = strstr(n, ".INI")))
977 		return 0;
978 	return 1;
979 }
980 
lang_code(const char * str)981 static char *lang_code(const char *str)
982 {
983 	char *p = strdup(str);
984 	if (!p)
985 		return NULL;
986 	p[strcspn(p, ".")] = 0;
987 	tolow(p);
988 	return p;
989 }
990 
991 
lang_name(const char * path,const char * file)992 static char *lang_name(const char *path, const char *file)
993 {
994 	char *l;
995 	l = lookup_tag(path, "Name", ";");
996 	if (l)
997 		return l;
998 	return lang_code(file);
999 }
1000 
1001 
cmp_lang(const void * p1,const void * p2)1002 static int cmp_lang(const void *p1, const void *p2)
1003 {
1004 	const struct lang *l1 = (const struct lang*)p1;
1005 	const struct lang *l2 = (const struct lang*)p2;
1006 	return strcmp(l1->name, l2->name);
1007 }
1008 
langs_sort()1009 static void langs_sort()
1010 {
1011 	qsort(langs, langs_nr, sizeof(struct lang), cmp_lang);
1012 }
1013 
menu_langs_lookup(const char * path)1014 int menu_langs_lookup(const char *path)
1015 {
1016 	char *p;
1017 	int n = 0, i = 0;
1018 	DIR *d;
1019 	struct dirent *de;
1020 	struct lang *new_langs;
1021 
1022 	if (!path)
1023 		return 0;
1024 
1025 	d = opendir(path);
1026 	if (!d)
1027 		return -1;
1028 	while ((de = readdir(d))) {
1029 		if (!is_lang(path, de->d_name))
1030 			continue;
1031 		n ++;
1032 	}
1033 	if (!n)
1034 		goto out;
1035 	closedir(d); d = opendir(path);
1036 	if (!d)
1037 		return -1;
1038 	new_langs = realloc(langs, sizeof(struct lang) * (n + langs_nr));
1039 	if (!new_langs) {
1040 		closedir(d);
1041 		return -1;
1042 	}
1043 	langs = new_langs;
1044 
1045 	while ((de = readdir(d)) && i < n) {
1046 		if (!is_lang(path, de->d_name))
1047 			continue;
1048 		p = getfilepath(path, de->d_name);
1049 		langs[langs_nr].path = p;
1050 		langs[langs_nr].file = lang_code(de->d_name);
1051 		langs[langs_nr].name = lang_name(p, de->d_name);
1052 		langs_nr ++;
1053 		i ++;
1054 	}
1055 out:
1056 	langs_sort();
1057 	closedir(d);
1058 	return 0;
1059 }
1060 
menu_lang_select(const char * name)1061 int menu_lang_select(const char *name)
1062 {
1063 	int i;
1064 	char cwd[PATH_MAX];
1065 	if (!name)
1066 		return -1;
1067 	getdir(cwd, sizeof(cwd));
1068 	setdir(game_cwd);
1069 	for (i = 0; i<langs_nr; i ++) {
1070 		if (!strcmp(langs[i].file, name)) {
1071 			lang_free();
1072 			if (lang_parse(langs[i].path) || lang_ok()) {
1073 				fprintf(stderr,"Error while loading language: %s\n", langs[i].file);
1074 				setdir(cwd);
1075 				return -1;
1076 			}
1077 			cur_lang = i;
1078 			FREE(opt_lang); opt_lang = strdup(langs[i].file);
1079 			setdir(cwd);
1080 			return 0;
1081 		}
1082 	}
1083 	setdir(cwd);
1084 	return -1;
1085 }
1086