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