1 /*
2  * Copyright 2009-2021 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 int theme_relative = 0;
29 
30 char	*curtheme_dir[2] = { NULL, NULL };
31 struct theme *curtheme_loading = NULL;
32 
parse_win_align(const char * v,void * data)33 static int parse_win_align(const char *v, void *data)
34 {
35 	int *i = (int *)data;
36 	if (!strcmp(v, "left"))
37 		*i = ALIGN_LEFT;
38 	else if (!strcmp(v, "justify"))
39 		*i = ALIGN_JUSTIFY;
40 	else if (!strcmp(v, "center"))
41 		*i = ALIGN_CENTER;
42 	else if (!strcmp(v, "right"))
43 		*i = ALIGN_RIGHT;
44 	else
45 		return -1;
46 	return 0;
47 }
48 
parse_gfx_mode(const char * v,void * data)49 static int parse_gfx_mode(const char *v, void *data)
50 {
51 	int *i = (int *)data;
52 	if (!strcmp(v, "fixed"))
53 		*i = GFX_MODE_FIXED;
54 	else if (!strcmp(v, "embedded"))
55 		*i = GFX_MODE_EMBEDDED;
56 	else if (!strncmp(v, "float", 5)) {
57 		*i = GFX_MODE_FLOAT;
58 		v += 5;
59 		if (!*v) /* compat */
60 			*i |= GFX_ALIGN_SET(ALIGN_TOP);
61 		while (*v) {
62 			if (*v != '-')
63 				return -1;
64 			v ++;
65 			if (!strncmp(v, "top", 3)) {
66 				*i |= GFX_ALIGN_SET(ALIGN_TOP);
67 				v += 3;
68 			} else if (!strncmp(v, "middle", 6)) {
69 				*i |= GFX_ALIGN_SET(ALIGN_MIDDLE);
70 				v += 6;
71 			} else if (!strncmp(v, "bottom", 6)) {
72 				*i |= GFX_ALIGN_SET(ALIGN_BOTTOM);
73 				v += 6;
74 			} else if (!strncmp(v, "left", 4)) {
75 				*i |= GFX_ALIGN_SET(ALIGN_LEFT);
76 				v += 4;
77 			} else if (!strncmp(v, "right", 5)) {
78 				*i |= GFX_ALIGN_SET(ALIGN_RIGHT);
79 				v += 5;
80 			} else if (!strncmp(v, "center", 6)) {
81 				*i |= GFX_ALIGN_SET(ALIGN_CENTER);
82 				v += 6;
83 			} else
84 				return -1;
85 		}
86 	} else if (!strcmp(v, "direct"))
87 		*i = GFX_MODE_DIRECT;
88 	else
89 		return -1;
90 	return 0;
91 }
92 
out_gfx_mode(const void * v,char ** out)93 static int out_gfx_mode(const void *v, char **out)
94 {
95 	char *o;
96 	char buff[256];
97 	int m = *((int*)v);
98 	switch (GFX_MODE(m)) {
99 	case GFX_MODE_FIXED:
100 		o = strdup("fixed");
101 		break;
102 	case GFX_MODE_EMBEDDED:
103 		o = strdup("embedded");
104 		break;
105 	case GFX_MODE_FLOAT:
106 		strcpy(buff, "float");
107 		if (GFX_ALIGN(m) != ALIGN_TOP) { /* compat */
108 			if (GFX_ALIGN(m) & ALIGN_TOP)
109 				strcat(buff,"-top");
110 			else if (GFX_ALIGN(m) & ALIGN_BOTTOM)
111 				strcat(buff,"-bottom");
112 			if (GFX_ALIGN(m) & ALIGN_LEFT)
113 				strcat(buff,"-left");
114 			else if (GFX_ALIGN(m) & ALIGN_RIGHT)
115 				strcat(buff,"-right");
116 		}
117 		o = strdup(buff);
118 		break;
119 	case GFX_MODE_DIRECT:
120 		o = strdup("direct");
121 		break;
122 	default:
123 		o = strdup("");
124 		break;
125 	}
126 	if (!o)
127 		return -1;
128 	*out = o;
129 	return 0;
130 }
131 
parse_inv_mode(const char * v,void * data)132 static int parse_inv_mode(const char *v, void *data)
133 {
134 	int *i = (int *)data;
135 	if (!strcmp(v, "vertical") || !strcmp(v, "0") || !strcmp(v, "vertical-left"))
136 		*i = INV_MODE_VERT | INV_ALIGN_SET(ALIGN_LEFT);
137 	else if (!strcmp(v, "horizontal") || !strcmp(v, "1") || !strcmp(v, "horizontal-center"))
138 		*i = INV_MODE_HORIZ | INV_ALIGN_SET(ALIGN_CENTER);
139 	else if (!strcmp(v, "horizontal-left") || !strcmp(v, "1"))
140 		*i = INV_MODE_HORIZ | INV_ALIGN_SET(ALIGN_LEFT);
141 	else if (!strcmp(v, "horizontal-right") || !strcmp(v, "1"))
142 		*i = INV_MODE_HORIZ | INV_ALIGN_SET(ALIGN_RIGHT);
143 	else if (!strcmp(v, "disabled") || !strcmp(v, "-1"))
144 		*i = INV_MODE_DISABLED;
145 	else if (!strcmp(v, "vertical-right"))
146 		*i = INV_MODE_VERT | INV_ALIGN_SET(ALIGN_RIGHT);
147 	else if (!strcmp(v, "vertical-center"))
148 		*i = INV_MODE_VERT | INV_ALIGN_SET(ALIGN_CENTER);
149 	else
150 		return -1;
151 	return 0;
152 }
153 
parse_ways_mode(const char * v,void * data)154 static int parse_ways_mode(const char *v, void *data)
155 {
156 	int *i = (int *)data;
157 	if (!strcmp(v, "top"))
158 		*i = ALIGN_TOP;
159 	else if (!strcmp(v, "bottom"))
160 		*i = ALIGN_BOTTOM;
161 	else
162 		return -1;
163 	return 0;
164 }
165 
out_ways_mode(const void * v,char ** out)166 static int out_ways_mode(const void *v, char **out)
167 {
168 	char *o;
169 	int m = *((int*)v);
170 	o = malloc(64);
171 	if (!o)
172 		return -1;
173 	if (m == ALIGN_BOTTOM)
174 		sprintf(o, "bottom");
175 	else
176 		sprintf(o, "top");
177 	*out = o;
178 	return 0;
179 }
180 
out_inv_mode(const void * v,char ** out)181 static int out_inv_mode(const void *v, char **out)
182 {
183 	char *o;
184 	int m = *((int*)v);
185 	o = malloc(64);
186 	if (!o)
187 		return -1;
188 	if (m == INV_MODE_DISABLED) {
189 		sprintf(o, "disabled");
190 		*out = o;
191 		return 0;
192 	}
193 
194 	if ((INV_MODE(m) == INV_MODE_HORIZ))
195 		sprintf(o, "horizontal");
196 	else
197 		sprintf(o, "vertical");
198 
199 	if ((m & INV_ALIGN_SET(ALIGN_CENTER)) == INV_ALIGN_SET(ALIGN_CENTER)) {
200 		strcat(o, "-center");
201 	} else if ((m & INV_ALIGN_SET(ALIGN_LEFT)) == INV_ALIGN_SET(ALIGN_LEFT)) {
202 		strcat(o, "-left");
203 	} else if ((m & INV_ALIGN_SET(ALIGN_RIGHT)) == INV_ALIGN_SET(ALIGN_RIGHT)) {
204 		strcat(o, "-right");
205 	}
206 	*out = o;
207 	return 0;
208 }
209 
parse_color(const char * v,void * data)210 static int parse_color(const char *v, void *data)
211 {
212 	color_t *c = (color_t *)data;
213 	return gfx_parse_color(v, c);
214 }
215 
out_color(const void * v,char ** out)216 static int out_color(const void *v, char **out)
217 {
218 	char *o;
219 	color_t *c = (color_t *)v;
220 	o = malloc(16);
221 	if (!o)
222 		return -1;
223 	sprintf(o, "#%02x%02x%02x", c->r, c->g, c->b);
224 	*out = o;
225 	return 0;
226 }
227 
parse_dpi(const char * v,void * data)228 int parse_dpi(const char *v, void *data)
229 {
230 	struct dpi *dpi = (struct dpi*)data;
231 	if (sscanf(v, "%d-%d", &dpi->min, &dpi->max) != 2) {
232 		if (sscanf(v, "%d", &dpi->min) != 1) {
233 			dpi->min = DEFAULT_DPI_MIN;
234 			dpi->max = DEFAULT_DPI_MAX;
235 			return -1;
236 		}
237 		dpi->max = dpi->min;
238 	}
239 	return 0;
240 }
241 
out_dpi(const void * v,char ** out)242 static int out_dpi(const void *v, char **out)
243 {
244 	char *o;
245 	struct dpi *dpi = (struct dpi *)v;
246 	o = malloc(64);
247 	if (!o)
248 		return -1;
249 	if (dpi->min == dpi->max)
250 		sprintf(o, "%d", dpi->min);
251 	else
252 		sprintf(o, "%d-%d", dpi->min, dpi->max);
253 	*out = o;
254 	return 0;
255 }
256 
parse_include(const char * v,void * data)257 static int parse_include(const char *v, void *data)
258 {
259 	int rc;
260 	char cwd[PATH_MAX];
261 	if (!strlowcmp(v, DEFAULT_THEME))
262 		return 0;
263 	if (curtheme_loading && curtheme_loading->type == THEME_GAME
264 		&& strlowcmp(v, curtheme_loading->dir)
265 		&& theme_lookup(v, THEME_GAME)) { /* internal theme? */
266 		return game_theme_load(v, THEME_GAME);
267 	}
268 	getdir(cwd, sizeof(cwd));
269 	setdir(game_cwd);
270 	rc = game_theme_load(v, THEME_GLOBAL);
271 /*	if (!rc)
272 		game_theme_select(v); */
273 	setdir(cwd);
274 	return rc;
275 }
276 
theme_parse_full_path(const char * v,void * data)277 static int theme_parse_full_path(const char *v, void *data)
278 {
279 	int rc;
280 	char **p = (char **)data;
281 	char *np;
282 	if (theme_relative) {
283 		if (!strncmp(v, "blank:", 6) ||
284 			!strncmp(v, "box:", 4) ||
285 			!strncmp(v, "spr:", 4)) /* hack for special files*/
286 			return parse_path(v, data);
287 		rc = parse_path(v, data);
288 		if (rc || !*p || !*p[0])
289 			return rc;
290 
291 		if (curtheme_loading && curtheme_loading->type == THEME_GAME) {
292 			np = getfilepath(curtheme_loading->path, *p);
293 			if (!*np)
294 				return -1;
295 			free(*p); *p = np;
296 		}
297 		return 0;
298 	}
299 	return parse_full_path(v, data);
300 }
301 
302 struct parser cmd_parser[] = {
303 	{ "scr.w", parse_int, &game_theme.w, 0 }, /* must be 0 */
304 	{ "scr.h", parse_int, &game_theme.h, 0 }, /* must be 1, see scale_aware logic */
305 	{ "scr.scale_aware", parse_int, &game_theme.scale_aware, 0 },
306 	{ "scr.dpi", parse_dpi, &game_theme.dpi, 0 },
307 	{ "scr.gfx.scalable", parse_int, &game_theme.gfx_scalable, CHANGED_ALL },
308 	{ "scr.gfx.scale", parse_float, &game_theme.img_scale, CHANGED_IMG },
309 	{ "scr.col.bg", parse_color, &game_theme.bgcol, 0 },
310 	{ "scr.col.brd", parse_color, &game_theme.brdcol, 0 },
311 	{ "scr.gfx.icon", theme_parse_full_path, &game_theme.icon_name, CHANGED_ICON },
312 	{ "scr.gfx.bg", theme_parse_full_path, &game_theme.bg_name, CHANGED_BG },
313 	{ "scr.gfx.cursor.normal", theme_parse_full_path, &game_theme.cursor_name, CHANGED_CURSOR },
314 	{ "scr.gfx.cursor.x", parse_int, &game_theme.cur_x, 0 },
315 	{ "scr.gfx.cursor.y", parse_int, &game_theme.cur_y, 0 },
316 	{ "scr.gfx.use", theme_parse_full_path, &game_theme.use_name, CHANGED_USE }, /* compat */
317 	{ "scr.gfx.cursor.use", theme_parse_full_path, &game_theme.use_name, CHANGED_USE },
318 	{ "scr.gfx.pad", parse_int, &game_theme.pad, CHANGED_WIN | CHANGED_INV },
319 	{ "scr.gfx.x", parse_int, &game_theme.gfx_x, 0 },
320 	{ "scr.gfx.y", parse_int, &game_theme.gfx_y, 0 },
321 	{ "scr.gfx.w", parse_int, &game_theme.max_scene_w, 0 },
322 	{ "scr.gfx.h", parse_int, &game_theme.max_scene_h, 0 },
323 	{ "scr.gfx.mode", parse_gfx_mode, &game_theme.gfx_mode, CHANGED_WIN },
324 
325 	{ "win.align", parse_win_align, &game_theme.win_align, CHANGED_WIN },
326 	{ "win.x", parse_int, &game_theme.win_x, CHANGED_WIN },
327 	{ "win.y", parse_int, &game_theme.win_y, CHANGED_WIN },
328 	{ "win.w", parse_int, &game_theme.win_w, CHANGED_WIN },
329 	{ "win.h", parse_int, &game_theme.win_h, CHANGED_WIN },
330 	{ "win.ways.mode", parse_ways_mode, &game_theme.ways_mode, CHANGED_WIN },
331 	{ "win.scroll.mode", parse_int, &game_theme.win_scroll_mode, CHANGED_WIN },
332 
333 	{ "win.fnt.name", theme_parse_full_path, &game_theme.font_name, CHANGED_FONT | CHANGED_WIN },
334 	{ "win.fnt.size", parse_int, &game_theme.font_size, CHANGED_FONT | CHANGED_WIN },
335 	{ "win.fnt.height", parse_float, &game_theme.font_height, CHANGED_WIN },
336 /* compat mode directive */
337 	{ "win.gfx.h", parse_int, &game_theme.max_scene_h, CHANGED_WIN },
338 /* here it was */
339 	{ "win.gfx.up", theme_parse_full_path, &game_theme.a_up_name, CHANGED_UP | CHANGED_WIN},
340 	{ "win.gfx.down", theme_parse_full_path, &game_theme.a_down_name, CHANGED_DOWN | CHANGED_WIN},
341 	{ "win.up.x", parse_int, &game_theme.a_up_x, CHANGED_WIN },
342 	{ "win.up.y", parse_int, &game_theme.a_up_y, CHANGED_WIN },
343 	{ "win.down.x", parse_int, &game_theme.a_down_x, CHANGED_WIN },
344 	{ "win.down.y", parse_int, &game_theme.a_down_y, CHANGED_WIN },
345 	{ "win.col.fg", parse_color, &game_theme.fgcol, CHANGED_WIN },
346 	{ "win.col.link", parse_color, &game_theme.lcol, CHANGED_WIN },
347 	{ "win.col.alink", parse_color, &game_theme.acol, CHANGED_WIN },
348 
349 	{ "inv.x", parse_int, &game_theme.inv_x, CHANGED_INV },
350 	{ "inv.y", parse_int, &game_theme.inv_y, CHANGED_INV },
351 	{ "inv.w", parse_int, &game_theme.inv_w, CHANGED_INV },
352 	{ "inv.h", parse_int, &game_theme.inv_h, CHANGED_INV },
353 	{ "inv.mode", parse_inv_mode, &game_theme.inv_mode, CHANGED_INV },
354 	{ "inv.horiz", parse_inv_mode, &game_theme.inv_mode, CHANGED_INV },
355 
356 	{ "inv.col.fg", parse_color, &game_theme.icol, CHANGED_INV },
357 	{ "inv.col.link", parse_color, &game_theme.ilcol, CHANGED_INV },
358 	{ "inv.col.alink", parse_color, &game_theme.iacol, CHANGED_INV },
359 	{ "inv.fnt.name", theme_parse_full_path, &game_theme.inv_font_name, CHANGED_IFONT | CHANGED_INV },
360 	{ "inv.fnt.size", parse_int, &game_theme.inv_font_size, CHANGED_IFONT | CHANGED_INV },
361 	{ "inv.fnt.height", parse_float, &game_theme.inv_font_height, CHANGED_INV },
362 	{ "inv.gfx.up", theme_parse_full_path, &game_theme.inv_a_up_name, CHANGED_IUP | CHANGED_INV },
363 	{ "inv.gfx.down", theme_parse_full_path, &game_theme.inv_a_down_name, CHANGED_IDOWN | CHANGED_INV},
364 	{ "inv.up.x", parse_int, &game_theme.inv_a_up_x, CHANGED_INV },
365 	{ "inv.up.y", parse_int, &game_theme.inv_a_up_y, CHANGED_INV },
366 	{ "inv.down.x", parse_int, &game_theme.inv_a_down_x, CHANGED_INV },
367 	{ "inv.down.y", parse_int, &game_theme.inv_a_down_y, CHANGED_INV },
368 
369 	{ "menu.col.bg", parse_color, &game_theme.menu_bg, 0 },
370 	{ "menu.col.fg", parse_color, &game_theme.menu_fg, 0 },
371 	{ "menu.col.link", parse_color, &game_theme.menu_link, 0 },
372 	{ "menu.col.alink", parse_color, &game_theme.menu_alink, 0 },
373 	{ "menu.col.alpha", parse_int, &game_theme.menu_alpha, 0 },
374 	{ "menu.col.border", parse_color, &game_theme.border_col, 0 },
375 	{ "menu.bw", parse_int, &game_theme.border_w, 0 },
376 	{ "menu.fnt.name", theme_parse_full_path, &game_theme.menu_font_name, CHANGED_MFONT },
377 	{ "menu.fnt.size", parse_int, &game_theme.menu_font_size, CHANGED_MFONT },
378 	{ "menu.fnt.height", parse_float, &game_theme.menu_font_height, 0 },
379 	{ "menu.gfx.button", theme_parse_full_path, &game_theme.menu_button_name, CHANGED_BUTTON },
380 	{ "menu.button.x", parse_int, &game_theme.menu_button_x, 0 },
381 	{ "menu.button.y", parse_int, &game_theme.menu_button_y, 0 },
382 /* compat */
383 	{ "menu.buttonx", parse_int, &game_theme.menu_button_x, 0 },
384 	{ "menu.buttony", parse_int, &game_theme.menu_button_y, 0 },
385 
386 	{ "snd.click", theme_parse_full_path, &game_theme.click_name, CHANGED_CLICK },
387 	{ "include", parse_include, NULL, 0 },
388 	{ NULL, NULL, NULL, 0 },
389 };
390 
391 #define TF_POSX	1
392 #define TF_POSY	2
393 #define TF_NEG	4
394 
395 typedef struct {
396 	const char *name;
397 	int *val;
398 	int flags;
399 } theme_scalable_t;
400 
401 static theme_scalable_t theme_scalables[] = {
402 	{ "scr.w", &game_theme.w, 0 },
403 	{ "scr.h", &game_theme.h, 0 },
404 	{ "scr.gfx.cursor.x", &game_theme.cur_x, 0 },
405 	{ "scr.gfx.cursor.y", &game_theme.cur_y, 0 },
406 	{ "scr.gfx.pad", &game_theme.pad, 0 },
407 	{ "scr.gfx.x", &game_theme.gfx_x, TF_POSX },
408 	{ "scr.gfx.y", &game_theme.gfx_y, TF_POSY },
409 	{ "scr.gfx.w", &game_theme.max_scene_w, TF_NEG },
410 	{ "scr.gfx.h", &game_theme.max_scene_h, TF_NEG },
411 	{ "win.x", &game_theme.win_x, TF_POSX },
412 	{ "win.y", &game_theme.win_y, TF_POSY },
413 	{ "win.w", &game_theme.win_w, 0 },
414 	{ "win.h", &game_theme.win_h, 0 },
415 	{ "win.fnt.size", &game_theme.font_size, 0 },
416 	{ "inv.x", &game_theme.inv_x, TF_POSX },
417 	{ "inv.y", &game_theme.inv_y, TF_POSY },
418 	{ "inv.w", &game_theme.inv_w, 0 },
419 	{ "inv.h", &game_theme.inv_h, 0 },
420 	{ "inv.fnt.size", &game_theme.inv_font_size, 0 },
421 	{ "menu.fnt.size", &game_theme.menu_font_size, 0 },
422 	{ "menu.button.x", &game_theme.menu_button_x, TF_POSX },
423 	{ "menu.button.y", &game_theme.menu_button_y, TF_POSY },
424 	{ "win.up.x", &game_theme.a_up_x, TF_POSX | TF_NEG },
425 	{ "win.up.y", &game_theme.a_up_y, TF_POSY | TF_NEG },
426 	{ "win.down.x", &game_theme.a_down_x, TF_POSX | TF_NEG },
427 	{ "win.down.y", &game_theme.a_down_y, TF_POSY | TF_NEG },
428 	{ "inv.up.x", &game_theme.inv_a_up_x, TF_POSX | TF_NEG },
429 	{ "inv.up.y", &game_theme.inv_a_up_y, TF_POSY | TF_NEG },
430 	{ "inv.down.x", &game_theme.inv_a_down_x, TF_POSX | TF_NEG },
431 	{ "inv.down.y", &game_theme.inv_a_down_y, TF_POSY | TF_NEG },
432 	{ NULL, NULL, 0 },
433 };
434 static int theme_scalables_unscaled[sizeof(theme_scalables)/sizeof(theme_scalable_t)];
435 
436 struct game_theme game_theme;
437 
free_theme_strings(void)438 static void free_theme_strings(void)
439 {
440 	struct game_theme *t = &game_theme;
441 	FREE(t->use_name);
442 	FREE(t->icon_name);
443 	FREE(t->cursor_name);
444 	FREE(t->bg_name);
445 	FREE(t->inv_a_up_name);
446 	FREE(t->inv_a_down_name);
447 	FREE(t->a_down_name);
448 	FREE(t->a_up_name);
449 	FREE(t->font_name);
450 	FREE(t->inv_font_name);
451 	FREE(t->menu_font_name);
452 	FREE(t->menu_button_name);
453 	FREE(t->click_name);
454 }
455 
game_theme_free(void)456 int game_theme_free(void)
457 {
458 	free_theme_strings();
459 
460 	if (game_theme.font)
461 		fnt_free(game_theme.font);
462 	if (game_theme.inv_font)
463 		fnt_free(game_theme.inv_font);
464 	if (game_theme.menu_font)
465 		fnt_free(game_theme.menu_font);
466 
467 	if (game_theme.a_up)
468 		gfx_free_image(game_theme.a_up);
469 	if (game_theme.a_down)
470 		gfx_free_image(game_theme.a_down);
471 	if (game_theme.inv_a_up)
472 		gfx_free_image(game_theme.inv_a_up);
473 	if (game_theme.inv_a_down)
474 		gfx_free_image(game_theme.inv_a_down);
475 
476 	if (game_theme.use)
477 		gfx_free_image(game_theme.use);
478 	if (game_theme.cursor) {
479 		gfx_free_image(game_theme.cursor);
480 		gfx_set_cursor(NULL, 0, 0);
481 	}
482 	if (game_theme.bg)
483 		gfx_free_image(game_theme.bg);
484 
485 	if (game_theme.menu_button)
486 		gfx_free_image(game_theme.menu_button);
487 
488 	if (game_theme.click) {
489 		sound_put(game_theme.click);
490 	}
491 
492 	if (game_theme.icon) {
493 		gfx_set_icon(NULL);
494 		gfx_free_image(game_theme.icon);
495 	}
496 
497 	game_theme.font = game_theme.inv_font = game_theme.menu_font = NULL;
498 	game_theme.a_up = game_theme.a_down = game_theme.use = NULL;
499 	game_theme.inv_a_up = game_theme.inv_a_down = NULL;
500 	game_theme.menu_button = NULL;
501 	game_theme.bg = NULL;
502 	game_theme.click = NULL;
503 	game_theme.cur_x = game_theme.cur_y = 0;
504 	game_theme.cursor = game_theme.use = NULL;
505 	game_theme.icon = NULL;
506 	return 0;
507 }
508 
theme_img_scale(img_t * p)509 int theme_img_scale(img_t *p)
510 {
511 	return theme_gfx_scale(p, game_theme.img_scale);
512 }
513 
theme_gfx_scale(img_t * p,float scale)514 int theme_gfx_scale(img_t *p, float scale)
515 {
516 	img_t pic;
517 	float v = game_theme.scale * scale;
518 	if (!p || !*p || v == 1.0f)
519 		return 0;
520 
521 	if (!cache_have(gfx_image_cache(), *p))
522 		return 0; /* do not scale sprites! */
523 
524 	pic = gfx_scale(*p, v, v, SCALABLE_THEME_SMOOTH);
525 	if (!pic)
526 		return -1;
527 	gfx_free_image(*p);
528 	*p = pic;
529 	return 0;
530 }
531 
game_theme_scale(int w,int h)532 static  int game_theme_scale(int w, int h)
533 {
534 	int i;
535 	float xs, ys, v;
536 	int xoff, yoff;
537 	struct game_theme *t = &game_theme;
538 
539 	if ((w < 0 || h < 0) && opt_hires) {
540 		int ww = 0, hh = 0;
541 		float dpi = gfx_get_dpi();
542 		if (dpi > game_theme.dpi.max && game_theme.dpi.max > 0)
543 			dpi = dpi / (float)game_theme.dpi.max;
544 		else if (dpi < game_theme.dpi.min && game_theme.dpi.min > 0)
545 			dpi = dpi / (float)game_theme.dpi.min;
546 		else
547 			dpi = 1.0f;
548 		if (dpi != 1.0f && !gfx_get_max_mode(&ww, &hh, MODE_ANY)) {
549 			w = t->w * dpi; h = t->h * dpi;
550 			xs = ys = dpi;
551 			if (w > ww)
552 				xs = (float)ww / (float)t->w;
553 			if (h > hh)
554 				ys = (float)hh / (float)t->h;
555 			v = (xs < ys)?xs:ys;
556 			w = t->w * v; h = t->h * v;
557 			printf("DPI scale: %f\n", v);
558 		}
559 	}
560 	if (w < 0 || h < 0 || (w == t->w && h == t->h)) {
561 		t->scale = 1.0f;
562 		t->xoff = 0;
563 		t->yoff = 0;
564 		w = t->w;
565 		h = t->h;
566 		goto out;
567 	}
568 
569 
570 	xs = (float)w / (float)t->w;
571 	ys = (float)h / (float)t->h;
572 
573 	v = (xs < ys)?xs:ys;
574 
575 	if (!SCALABLE_THEME) {
576 		if (v > 1.0f) {
577 			int ff = 1;
578 			while (ff && ff <= v && ff < 0x1000)
579 				ff <<= 1;
580 			ff >>= 1;
581 			v = ff;
582 		} else {
583 			float f = ceil(1.0f / v);
584 			int ff = 1;
585 			while (ff && ff < f && ff < 0x1000)
586 				ff <<= 1;
587 			v = 1.0f / (float)ff;
588 		}
589 	}
590 
591 	xoff = (w - t->w*v)/2;
592 	yoff = (h - t->h*v)/2;
593 
594 	if (xoff < 0)
595 		xoff = 0;
596 	if (yoff < 0)
597 		yoff = 0;
598 
599 	t->scale = v;
600 	t->xoff = xoff;
601 	t->yoff = yoff;
602 out:
603 	for (i = 0; theme_scalables[i].name; i++) {
604 		int val = *(theme_scalables[i].val);
605 		theme_scalables_unscaled[i] = val;
606 		if (val == -1 && (theme_scalables[i].flags & TF_NEG))
607 			continue;
608 		val *= t->scale;
609 		if (theme_scalables[i].flags & TF_POSX)
610 			val += t->xoff;
611 		if (theme_scalables[i].flags & TF_POSY)
612 			val += t->yoff;
613 		*(theme_scalables[i].val) = val;
614 	}
615 	t->w = w;
616 	t->h = h;
617 	if (t->scale_aware) {
618 		if (t->scale_aware == 2) {
619 			t->img_scale *= t->scale;
620 			t->scale = 1.0f;
621 		}
622 		t->xoff = t->yoff = 0;
623 		theme_scalables_unscaled[0] = w / t->scale;
624 		theme_scalables_unscaled[1] = h / t->scale;
625 		for (i = 2; theme_scalables[i].name; i++)
626 			theme_scalables_unscaled[i] = *(theme_scalables[i].val) / t->scale;
627 	}
628 	return 0;
629 }
630 extern int parse_relative_path;
631 
theme_getvar(char * name)632 char *theme_getvar(char *name)
633 {
634 	int i;
635 	for (i = 0; theme_scalables[i].name; i ++) {
636 		int val;
637 		char buf[64];
638 		if (strcmp(theme_scalables[i].name, name))
639 			continue;
640 		val = theme_scalables_unscaled[i];
641 		sprintf(buf, "%d", val);
642 		return strdup(buf);
643 	}
644 	/* so, it is a string or like this */
645 	for (i = 0; cmd_parser[i].cmd; i++) {
646 		int *num;
647 		char *s;
648 		float *f;
649 		char buf[64];
650 		if (strcmp(cmd_parser[i].cmd, name))
651 			continue;
652 		if (cmd_parser[i].fn == parse_int) {
653 			num = (int *)cmd_parser[i].p;
654 			sprintf(buf, "%d", *num);
655 			return strdup(buf);
656 		} else if (cmd_parser[i].fn == theme_parse_full_path) {
657 			s = *((char **)cmd_parser[i].p);
658 			if (!s)
659 				return NULL;
660 			return strdup(s);
661 		} else if (cmd_parser[i].fn == parse_inv_mode) {
662 			if (out_inv_mode(cmd_parser[i].p, &s))
663 				return NULL;
664 			return s;
665 		} else if (cmd_parser[i].fn == parse_ways_mode) {
666 			if (out_ways_mode(cmd_parser[i].p, &s))
667 				return NULL;
668 			return s;
669 		} else if (cmd_parser[i].fn == parse_gfx_mode) {
670 			if (out_gfx_mode(cmd_parser[i].p, &s))
671 				return NULL;
672 			return s;
673 		} else if (cmd_parser[i].fn == parse_float) {
674 			f = (float*)cmd_parser[i].p;
675 			sprintf(buf, "%f", *f);
676 			return strdup(buf);
677 		} else if (cmd_parser[i].fn == parse_color) {
678 			if (out_color(cmd_parser[i].p, &s))
679 				return NULL;
680 			return s;
681 		} else if (cmd_parser[i].fn == parse_dpi) {
682 			if (out_dpi(cmd_parser[i].p, &s))
683 				return NULL;
684 			return s;
685 		} else
686 			return NULL;
687 		break;
688 	}
689 	return NULL;
690 }
691 
theme_process_cmd(char * n,char * v,struct parser * cmd_parser)692 static int theme_process_cmd(char *n, char *v, struct parser *cmd_parser)
693 {
694 	int i;
695 	n = strip(n);
696 	v = strip(v);
697 
698 	if (process_cmd(n, v, cmd_parser))
699 		return -1;
700 
701 	for (i = 0; cmd_parser[i].cmd; i++) {
702 		if (!strcmp(cmd_parser[i].cmd, n)) {
703 			game_theme.changed |= cmd_parser[i].aux;
704 			return 0;
705 		}
706 	}
707 
708 	return -1;
709 }
710 
theme_setvar(char * name,char * val)711 int theme_setvar(char *name, char *val)
712 {
713 	int rc = -1;
714 	struct game_theme *t = &game_theme;
715 	theme_relative = 1;
716 	if (!theme_process_cmd(name, val, cmd_parser)) {
717 		int i;
718 		for (i = 0; theme_scalables[i].name; i++) {
719 			int val;
720 			if (strcmp(theme_scalables[i].name, name))
721 				continue;
722 			val = *(theme_scalables[i].val);
723 			theme_scalables_unscaled[i] = val;
724 			if (val == -1 && (theme_scalables[i].flags & TF_NEG))
725 				continue;
726 			val *= t->scale;
727 			if (theme_scalables[i].flags & TF_POSX)
728 				val += t->xoff;
729 	  		if (theme_scalables[i].flags & TF_POSY)
730 				val += t->yoff;
731 			*(theme_scalables[i].val) = val;
732 			break;
733 		}
734 		rc = 0;
735 	}
736 	theme_relative = 0;
737 	return rc;
738 }
739 
theme_bg_scale(void)740 static int theme_bg_scale(void)
741 {
742 	struct game_theme *t = &game_theme;
743 	if (t->bg) {
744 		img_t screen, pic;
745 		int xoff = (t->w - gfx_img_w(t->bg))/2;
746 		int yoff = (t->h - gfx_img_h(t->bg))/2;
747 		if (xoff < 0)
748 			xoff = 0;
749 		if (yoff < 0)
750 			yoff = 0;
751 		if (gfx_img_w(t->bg) < t->w || gfx_img_h(t->bg) < t->h) {
752 			pic = gfx_new(t->w, t->h);
753 			if (!pic)
754 				return -1;
755 			screen = gfx_screen(pic);
756 			gfx_img_fill(pic, 0, 0, t->w, t->h,
757 				gfx_col(t->brdcol.r, t->brdcol.g, t->brdcol.b));
758 			gfx_copy(t->bg, xoff, yoff);
759 			gfx_screen(screen);
760 			gfx_free_image(t->bg);
761 			t->bg = pic;
762 		}
763 	}
764 	return 0;
765 }
766 
game_theme_optimize(void)767 int game_theme_optimize(void)
768 {
769 /* todo: check errors */
770 	struct game_theme *t = &game_theme;
771 
772 	if (t->bg && cache_have(gfx_image_cache(), t->bg)) {
773 		t->bg = gfx_display_alpha(t->bg);
774 		gfx_unset_alpha(t->bg);
775 	}
776 	if (t->a_up && cache_have(gfx_image_cache(), t->a_up))
777 		t->a_up = gfx_display_alpha(t->a_up);
778 	if (t->a_down && cache_have(gfx_image_cache(), t->a_down))
779 		t->a_down = gfx_display_alpha(t->a_down);
780 	if (t->inv_a_up && cache_have(gfx_image_cache(), t->inv_a_up))
781 		t->inv_a_up = gfx_display_alpha(t->inv_a_up);
782 	if (t->inv_a_down && cache_have(gfx_image_cache(), t->inv_a_down))
783 		t->inv_a_down = gfx_display_alpha(t->inv_a_down);
784 	if (t->use && cache_have(gfx_image_cache(), t->use))
785 		t->use = gfx_display_alpha(t->use);
786 	if (t->cursor && cache_have(gfx_image_cache(), t->cursor)) {
787 		t->cursor = gfx_display_alpha(t->cursor);
788 		gfx_set_cursor(t->cursor, t->cur_x, t->cur_y);
789 	}
790 	if (t->menu_button && cache_have(gfx_image_cache(), t->menu_button))
791 		t->menu_button = gfx_display_alpha(t->menu_button);
792 	return 0;
793 }
794 
game_theme_update_data(void)795 static int game_theme_update_data(void)
796 {
797 	struct game_theme *t = &game_theme;
798 	const char *res = NULL;
799 	int idf = idf_only(instead_idf(), 0);
800 	if (t->font_name && (t->changed & CHANGED_FONT)) {
801 		fnt_free(t->font);
802 		if (!(t->font = fnt_load(t->font_name, FONT_SZ(t->font_size)))) {
803 			res = t->font_name;
804 			goto err;
805 		}
806 	}
807 
808 	if (t->inv_font_name && (t->changed & CHANGED_IFONT)) {
809 		fnt_free(t->inv_font);
810 		if (!(t->inv_font = fnt_load(t->inv_font_name, FONT_SZ(t->inv_font_size)))) {
811 			res = t->inv_font_name;
812 			goto err;
813 		}
814 	}
815 
816 	if (t->menu_font_name && (t->changed & CHANGED_MFONT)) {
817 		int m = FONT_SZ(t->inv_font_size);
818 		if (MAX_MENU_LINES * m * game_theme.menu_font_height > game_theme.h)
819 			m = game_theme.h / MAX_MENU_LINES / game_theme.menu_font_height;
820 		else if (m < t->menu_font_size)
821 			m = t->menu_font_size;
822 /*		fprintf(stderr, "%d %d > %d? %d", (int)FONT_SZ(t->inv_font_size), (int)FONT_SZ(t->inv_font_size) * MAX_MENU_LINES, game_theme.h, m); */
823 		fnt_free(t->menu_font);
824 		if (!(t->menu_font = fnt_load(t->menu_font_name, m))) { /* do not scale menu!!! */
825 			res = t->menu_font_name;
826 			goto err;
827 		}
828 	}
829 
830 	if (t->a_up_name && (t->changed & CHANGED_UP)) {
831 		gfx_free_image(t->a_up);
832 		if (!(t->a_up = gfx_load_image(t->a_up_name))) {
833 			res = t->a_up_name;
834 			goto err;
835 		}
836 		if (theme_img_scale(&t->a_up))
837 			goto err;
838 	}
839 
840 	if (t->a_down_name && (t->changed & CHANGED_DOWN)) {
841 		gfx_free_image(t->a_down);
842 		if (!(t->a_down = gfx_load_image(t->a_down_name))) {
843 			res = t->a_down_name;
844 			goto err;
845 		}
846 		if (theme_img_scale(&t->a_down))
847 			goto err;
848 	}
849 
850 	if (t->inv_a_up_name && (t->changed & CHANGED_IUP)) {
851 		gfx_free_image(t->inv_a_up);
852 		if (!(t->inv_a_up = gfx_load_image(t->inv_a_up_name))) {
853 			res = t->inv_a_up_name;
854 			goto err;
855 		}
856 		if (theme_img_scale(&t->inv_a_up))
857 			goto err;
858 	}
859 
860 	if (t->inv_a_down_name && (t->changed & CHANGED_IDOWN)) {
861 		gfx_free_image(t->inv_a_down);
862 		if (!(t->inv_a_down = gfx_load_image(t->inv_a_down_name))) {
863 			res = t->inv_a_down_name;
864 			goto err;
865 		}
866 		if (theme_img_scale(&t->inv_a_down))
867 			goto err;
868 	}
869 
870 	if (t->bg_name && (t->changed & CHANGED_BG)) {
871 		gfx_free_image(t->bg);
872 		t->bg = NULL;
873 		if (t->bg_name[0] && !(t->bg = gfx_load_image(t->bg_name))) {
874 			res = t->bg_name;
875 			goto skip; /* not fatal */
876 		}
877 		if (theme_img_scale(&t->bg))
878 			goto err;
879 		if (theme_bg_scale())
880 			goto err;
881 	}
882 skip:
883 	if (t->icon_name && (t->changed & CHANGED_ICON)) {
884 		if (t->icon)
885 			gfx_free_image(t->icon);
886 		t->icon = gfx_load_image(t->icon_name);
887 	}
888 
889 	if (t->use_name && (t->changed & CHANGED_USE)) {
890 		gfx_free_image(t->use);
891 		if (!(t->use = gfx_load_image(t->use_name))) {
892 			res = t->use_name;
893 			goto err;
894 		}
895 		if (theme_img_scale(&t->use))
896 			goto err;
897 	}
898 
899 	if (t->cursor_name && (t->changed & CHANGED_CURSOR)) {
900 		gfx_free_image(t->cursor);
901 		if (!(t->cursor = gfx_load_image(t->cursor_name))) {
902 			res = t->cursor_name;
903 			goto err;
904 		}
905 		if (theme_img_scale(&t->cursor))
906 			goto err;
907 		gfx_set_cursor(t->cursor, t->cur_x, t->cur_y);
908 	}
909 
910 	if (t->menu_button_name && (t->changed & CHANGED_BUTTON)) {
911 		gfx_free_image(t->menu_button);
912 		if (!(t->menu_button = gfx_load_image(t->menu_button_name))) {
913 			res = t->menu_button_name;
914 			goto err;
915 		}
916 		if (theme_img_scale(&t->menu_button))
917 			goto err;
918 	}
919 
920 	if (t->click_name && (t->changed & CHANGED_CLICK)) {
921 		sound_put(t->click);
922 		t->click = sound_get(t->click_name);
923 	}
924 
925 /*	free_theme_strings(); */ /* todo, font */
926 	t->changed = 0;
927 	if (!t->cursor || !t->use || !t->inv_a_up || !t->inv_a_down || !t->a_down || !t->a_up ||
928 		!t->font || !t->inv_font || !t->menu_font || !t->menu_button) {
929 		fprintf(stderr,"Can't init theme. Not all required elements are defined.\n");
930 		goto err;
931 	}
932 	idf_only(instead_idf(), idf);
933 	return 0;
934 err:
935 	idf_only(instead_idf(), idf);
936 	t->changed = 0;
937 	game_res_err_msg(res, 1);
938 	return -1;
939 }
940 
game_theme_update(void)941 int game_theme_update(void)
942 {
943 	game_theme_changed = 0;
944 	game_release_theme(0);
945 	if (game_theme_update_data()) {
946 		fprintf(stderr, "Can not update theme!\n");
947 		game_release_theme(1);
948 		game_error();
949 		return -1;
950 	}
951 
952 	if (game_apply_theme()) {
953 		fprintf(stderr, "Can not apply theme!\n");
954 		game_error();
955 		return -1;
956 	}
957 	return 0;
958 }
959 
960 #if defined(ANDROID) || defined(IOS) || defined(WINRT) || defined(_USE_SWROTATE)
961 extern void rotate_landscape(void);
962 extern void rotate_portrait(void);
963 extern void unlock_rotation(void);
964 #endif
965 
game_theme_init(void)966 int game_theme_init(void)
967 {
968 	int w  = opt_mode[0];
969 	int h  = opt_mode[1];
970 
971 	game_cursor_show = 1;
972 
973 	if (opt_fs && opt_hires && !gfx_get_max_mode(&w, &h, MODE_ANY)) {
974 #if defined(IOS) || defined(ANDROID) || defined(WINRT) || defined(_USE_SWROTATE)
975 		if ((game_theme.w > game_theme.h && w < h) ||
976 		    (game_theme.w < game_theme.h && w > h)) { /* rotated */
977 			if (gfx_get_max_mode(&w, &h, (game_theme.w > game_theme.h)?MODE_H:MODE_V)) {
978 				gfx_get_max_mode(&w, &h, MODE_ANY); /* fallback to any mode */
979 			}
980 		}
981 		if (game_theme.w > game_theme.h)
982 			rotate_landscape();
983 		else if (game_theme.w < game_theme.h)
984 			rotate_portrait();
985 		else
986 			unlock_rotation();
987 #if defined(ANDROID)
988 		usleep(100000);
989 		gfx_get_max_mode(&w, &h, MODE_ANY);
990 #endif
991 #endif
992 	}
993 #if defined(ANDROID) || defined(IOS) || defined(WINRT) || defined(_USE_SWROTATE)
994 	else {
995 		unlock_rotation();
996 	}
997 #endif
998 
999 	if (w == -1) { /* as theme */
1000 #if !defined(IOS) && !defined(SAILFISHOS) /* always hardware accelerated */
1001 		if (gfx_get_max_mode(&w, &h, MODE_ANY) || (game_theme.w <= w && game_theme.h <= h)) {
1002 			w = opt_mode[0];
1003 			h = opt_mode[1];
1004 		}
1005 #endif
1006 	}
1007 	game_theme_scale(w, h);
1008 	if (gfx_set_mode(game_theme.w, game_theme.h, opt_fs)) {
1009 		opt_mode[0] = opt_mode[1] = -1; opt_fs = 0; /* safe options */
1010 		return -1;
1011 	}
1012 	if (game_theme_update_data()) {
1013 		fprintf(stderr, "Can not init theme!\n");
1014 		game_theme_free();
1015 		game_theme_select(DEFAULT_THEME);
1016 		return -1;
1017 	}
1018 	gfx_bg(game_theme.xoff, game_theme.yoff,
1019 		game_theme.w - 2*game_theme.xoff, game_theme.h - 2*game_theme.yoff,
1020 		game_theme.bgcol, game_theme.brdcol);
1021 	game_clear_all();
1022 	gfx_flip();
1023 	gfx_commit();
1024 	return 0;
1025 }
1026 
theme_parse_idf(idf_t idf,const char * path)1027 static int theme_parse_idf(idf_t idf, const char *path)
1028 {
1029 	idff_t idff = NULL;
1030 	if (idf)
1031 		idff = idf_open(idf, path);
1032 
1033 	if (idff) {
1034 		int rc = parse_idff(idff, path, cmd_parser);
1035 		idf_close(idff);
1036 		if (rc)
1037 			fprintf(stderr, "Theme parsed with errors!\n");
1038 		return rc;
1039 	}
1040 	if (parse_ini(dirpath(path), cmd_parser)) {
1041 		fprintf(stderr, "Theme parsed with errors!\n");
1042 /*		game_theme_free(); */
1043 		return -1;
1044 	}
1045 
1046 	return 0;
1047 }
1048 
theme_load(const char * name)1049 int theme_load(const char *name)
1050 {
1051 	idf_t idf = NULL;
1052 
1053 	if (theme_relative)
1054 		idf = instead_idf();
1055 
1056 	if (theme_parse_idf(idf, name))
1057 		return 0; /* no theme loaded if error in parsing */
1058 	return 0;
1059 }
1060 
theme_load_idf(idf_t idf,const char * name)1061 int theme_load_idf(idf_t idf, const char *name)
1062 {
1063 	if (theme_parse_idf(idf, name))
1064 		return 0; /* no theme loaded if error in parsing */
1065 	return 0;
1066 }
1067 
1068 struct	theme *themes = NULL;
1069 int themes_nr = 0;
1070 
is_theme_idf(idf_t idf,const char * path,const char * n)1071 static int is_theme_idf(idf_t idf, const char *path, const char *n)
1072 {
1073 	int rc = 0;
1074 	char *p = getpath(path, n);
1075 	char *pp;
1076 	if (!p)
1077 		return 0;
1078 	pp = malloc(strlen(p) + strlen(THEME_FILE) + 1);
1079 	if (pp) {
1080 		strcpy(pp, p);
1081 		strcat(pp, THEME_FILE);
1082 		if (idf)
1083 			rc = !idf_access(idf, pp);
1084 		else
1085 			rc = !access(pp, R_OK);
1086 		free(pp);
1087 	}
1088 	free(p);
1089 	return rc;
1090 }
1091 
is_theme(const char * path,const char * n)1092 static int is_theme(const char *path, const char *n)
1093 {
1094 	if (!n)
1095 		return 0;
1096 	if (!strcmp("..", n) || !strcmp(".", n))
1097 		return 0;
1098 	return is_theme_idf(NULL, path, n);
1099 }
1100 
theme_name(const char * path,const char * d_name)1101 static char *theme_name(const char *path, const char *d_name)
1102 {
1103 	char *l;
1104 	char *p = getfilepath(path, THEME_FILE);
1105 	if (!p)
1106 		goto err;
1107 	l = lookup_lang_tag(p, "Name", ";", opt_lang);
1108 	free(p);
1109 	if (l)
1110 		return l;
1111 err:
1112 	return strdup(d_name);
1113 }
1114 
theme_name_idf(idf_t idf,const char * path,const char * d_name)1115 static char *theme_name_idf(idf_t idf, const char *path, const char *d_name)
1116 {
1117 	char *l;
1118 	char *p = getfilepath(path, THEME_FILE);
1119 	if (!p)
1120 		goto err;
1121 	l = lookup_lang_tag_idf(idf, p, "Name", ";", opt_lang);
1122 	free(p);
1123 	if (l)
1124 		return l;
1125 err:
1126 	return strdup(d_name);
1127 }
1128 
cmp_theme(const void * p1,const void * p2)1129 static int cmp_theme(const void *p1, const void *p2)
1130 {
1131 	const struct theme *t1 = (const struct theme*)p1;
1132 	const struct theme *t2 = (const struct theme*)p2;
1133 	if (t1->type != t2->type)
1134 		return t1->type - t2->type;
1135 	return strcmp(t1->name, t2->name);
1136 }
1137 
themes_sort()1138 static void themes_sort()
1139 {
1140 	qsort(themes, themes_nr, sizeof(struct theme), cmp_theme);
1141 }
1142 
1143 struct theme *theme_lookup(const char *name, int type);
1144 
themes_count(int type)1145 int themes_count(int type)
1146 {
1147 	int rc = 0;
1148 	int i;
1149 	for (i = 0; i < themes_nr; i++)
1150 		rc = rc + (themes[i].type == type);
1151 	return rc;
1152 }
1153 
themes_max(int * type)1154 int themes_max(int *type)
1155 {
1156 	int n;
1157 	int count = themes_count(THEME_GLOBAL);
1158 
1159 	if (type)
1160 		*type = THEME_GLOBAL;
1161 
1162 	if ((n = themes_count(THEME_GAME)) > 0 && opt_owntheme) {
1163 		count = n;
1164 		if (type)
1165 			*type = THEME_GAME;
1166 	}
1167 
1168 	return count;
1169 }
1170 
themes_drop(int type)1171 void themes_drop(int type)
1172 {
1173 	int new_size;
1174 	struct theme *new_themes = NULL;
1175 	int rc, i, k = 0;
1176 	rc  = themes_count(type);
1177 	if (!rc)
1178 		return;
1179 	new_size = (themes_nr - rc) * sizeof(struct theme);
1180 	if (new_size)
1181 		new_themes = malloc(new_size);
1182 
1183 	if (!new_themes) {
1184 		fprintf(stderr, "Fatal: can't alloc memory.\n");
1185 		return;
1186 	}
1187 
1188 	for (i = 0; i < themes_nr; i ++) {
1189 		if (themes[i].type == type) {
1190 			free(themes[i].path);
1191 			free(themes[i].dir);
1192 			free(themes[i].name);
1193 		} else {
1194 			char *p = curtheme_dir[themes[i].type];
1195 			new_themes[k++] = themes[i];
1196 			if (p && !strlowcmp(p, themes[i].dir))
1197 				curtheme_dir[themes[i].type] = p;
1198 		}
1199 	}
1200 	themes_nr = k;
1201 	curtheme_dir[type] = NULL;
1202 
1203 	free(themes);
1204 	themes = new_themes;
1205 }
1206 
themes_lookup_idf(idf_t idf,const char * path,int type)1207 int themes_lookup_idf(idf_t idf, const char *path, int type)
1208 {
1209 	char *p;
1210 	idff_t idf_dir;
1211 	int n = 0, i = 0;
1212 	struct theme *new_themes;
1213 	char *idf_de;
1214 	if (!idf)
1215 		return -1;
1216 	idf_dir = idf_opendir(idf, path);
1217 	if (!idf_dir)
1218 		return -1;
1219 	while ((idf_de = idf_readdir(idf_dir))) {
1220 		if (theme_lookup(idf_de, type))
1221 			continue;
1222 		if (!is_theme_idf(idf, path, idf_de))
1223 			continue;
1224 		n ++;
1225 	}
1226 	if (!n)
1227 		goto out;
1228 	idf_closedir(idf_dir); idf_dir = idf_opendir(idf, path);
1229 	if (!idf_dir)
1230 		return -1;
1231 	new_themes = realloc(themes, sizeof(struct theme) * (n + themes_nr));
1232 	if (!new_themes) {
1233 		idf_closedir(idf_dir);
1234 		return -1;
1235 	}
1236 	themes = new_themes;
1237 	while ((idf_de = idf_readdir(idf_dir)) && i < n) {
1238 		if (theme_lookup(idf_de, type))
1239 			continue;
1240 		if (!is_theme_idf(idf, path, idf_de))
1241 			continue;
1242 		p = getpath(path, idf_de);
1243 		themes[themes_nr].path = p;
1244 		themes[themes_nr].dir = strdup(idf_de);
1245 		themes[themes_nr].name = theme_name_idf(idf, p, idf_de);
1246 		themes[themes_nr].type = type;
1247 		themes[themes_nr].idf = 1;
1248 		themes_nr ++;
1249 		i ++;
1250 	}
1251 out:
1252 	idf_closedir(idf_dir);
1253 	themes_sort();
1254 	return 0;
1255 }
1256 
themes_lookup(const char * path,int type)1257 int themes_lookup(const char *path, int type)
1258 {
1259 	char *p;
1260 	int n = 0, i = 0;
1261 	DIR *d;
1262 	struct dirent *de;
1263 	struct theme *new_themes;
1264 
1265 	if (!path)
1266 		return 0;
1267 
1268 	d = opendir(path);
1269 	if (!d)
1270 		return -1;
1271 	while ((de = readdir(d))) {
1272 		if (theme_lookup(de->d_name, type))
1273 			continue;
1274 		if (!is_theme(path, de->d_name))
1275 			continue;
1276 		n ++;
1277 	}
1278 	if (!n)
1279 		goto out;
1280 	closedir(d); d = opendir(path);
1281 	if (!d)
1282 		return -1;
1283 	new_themes = realloc(themes, sizeof(struct theme) * (n + themes_nr));
1284 	if (!new_themes) {
1285 		closedir(d);
1286 		return -1;
1287 	}
1288 	themes = new_themes;
1289 	while ((de = readdir(d)) && i < n) {
1290 		/*if (de->d_type != DT_DIR)
1291 			continue;*/
1292 		if (theme_lookup(de->d_name, type))
1293 			continue;
1294 		if (!is_theme(path, de->d_name))
1295 			continue;
1296 		p = getpath(path, de->d_name);
1297 		themes[themes_nr].path = p;
1298 		themes[themes_nr].dir = strdup(de->d_name);
1299 		themes[themes_nr].name = theme_name(p, de->d_name);
1300 		themes[themes_nr].type = type;
1301 		themes[themes_nr].idf = 0;
1302 		themes_nr ++;
1303 		i ++;
1304 	}
1305 out:
1306 	closedir(d);
1307 	themes_sort();
1308 	return 0;
1309 }
1310 
themes_rename(void)1311 int themes_rename(void)
1312 {
1313 	int i;
1314 	char cwd[PATH_MAX];
1315 	getdir(cwd, sizeof(cwd));
1316 	setdir(game_cwd);
1317 	for (i = 0; i < themes_nr; i++) {
1318 		FREE(themes[i].name);
1319 		themes[i].name = theme_name(dirpath(themes[i].path), themes[i].dir);
1320 	}
1321 	setdir(cwd);
1322 	return 0;
1323 }
1324 
theme_lookup(const char * name,int type)1325 struct theme *theme_lookup(const char *name, int type)
1326 {
1327 	int i;
1328 	if (!name || !*name) {
1329 		if (themes_nr == 1 && themes[0].type == type)
1330 			return &themes[0];
1331 		return NULL;
1332 	}
1333 	for (i = 0; i<themes_nr; i ++) {
1334 		if (!strlowcmp(themes[i].dir, name) && themes[i].type == type) {
1335 			return &themes[i];
1336 		}
1337 	}
1338 	return NULL;
1339 }
1340 
game_theme_load(const char * name,int type)1341 int game_theme_load(const char *name, int type)
1342 {
1343 	struct theme *otheme = curtheme_loading;
1344 	struct theme *theme;
1345 	struct game *game;
1346 	char cwd[PATH_MAX];
1347 	int rc = -1;
1348 	int rel = theme_relative;
1349 
1350 	getdir(cwd, sizeof(cwd));
1351 	setdir(game_cwd);
1352 
1353 	if (type == THEME_GLOBAL) {
1354 		theme_relative = 0;
1355 	} else {
1356 		game = game_lookup(curgame_dir);
1357 		if (!game)
1358 			return -1;
1359 		if (!game->idf)
1360 			setdir(game->path);
1361 		theme_relative = 1;
1362 	}
1363 
1364 	theme = theme_lookup(name, type);
1365 	if (!theme)
1366 		goto err;
1367 
1368 	if (theme->idf) /* cwd is always game_cwd */
1369 		strcpy(cwd, idf_getdir(instead_idf()));
1370 
1371 	curtheme_loading = theme;
1372 
1373 	if (theme->idf) {
1374 		if (idf_setdir(instead_idf(), theme->path))
1375 			goto err;
1376 		if (theme_load_idf(instead_idf(), THEME_FILE))
1377 			goto err;
1378 	} else {
1379 		if (setdir(theme->path))
1380 			goto err;
1381 		if (theme_load(THEME_FILE))
1382 			goto err;
1383 	}
1384 	rc = 0;
1385 err:
1386 	curtheme_loading = otheme;
1387 
1388 	if (theme && theme->idf)
1389 		idf_setdir(instead_idf(), cwd);
1390 	else
1391 		setdir(cwd);
1392 
1393 	theme_relative = rel;
1394 	return rc;
1395 }
1396 
game_theme_select(const char * name)1397 int game_theme_select(const char *name)
1398 {
1399 	struct theme *theme;
1400 	theme = theme_lookup(name, THEME_GAME);
1401 	if (theme) {
1402 		curtheme_dir[THEME_GAME] = theme->dir;
1403 		game_cfg_save();
1404 		return 0;
1405 	}
1406 	theme = theme_lookup(name, THEME_GLOBAL);
1407 	if (!theme)
1408 		return -1;
1409 	curtheme_dir[THEME_GLOBAL] = theme->dir;
1410 	return 0;
1411 }
1412 
game_default_theme(void)1413 int game_default_theme(void)
1414 {
1415 	int rc;
1416 	memset(&game_theme, 0, sizeof(game_theme));
1417 	game_theme.changed = CHANGED_ALL;
1418 	rc = game_theme_load(DEFAULT_THEME, THEME_GLOBAL);
1419 	if (rc)
1420 		return rc;
1421 	if (opt_owntheme && themes_count(THEME_GAME) > 0)
1422 		rc = game_theme_load(DEFAULT_THEME, THEME_GAME);
1423 	return rc;
1424 }
1425