Freeciv-3.3: /fast/freeciv/nightly/main/client/gui-sdl2/themespec.c Source File

Freeciv-3.3

Loading...
Searching...
No Matches
themespec.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/***********************************************************************
15 Functions for handling the themespec files which describe
16 the files and contents of themes.
17 original author: David Pfitzner <dwp@mso.anu.edu.au>
18***********************************************************************/
19
20#ifdef HAVE_CONFIG_H
21#include <fc_config.h>
22#endif
23
24#include <stdio.h>
25#include <stdlib.h> /* exit */
26#include <string.h>
27
28/* utility */
29#include "capability.h"
30#include "fcintl.h"
31#include "log.h"
32#include "registry.h"
33#include "string_vector.h"
34
35/* common */
36#include "game.h"
37
38/* client */
39#include "citydlg_common.h"
40#include "client_main.h" /* for client_state() */
41
42/* gui-sdl2 */
43#include "dialogs.h"
44#include "gui_tilespec.h"
45#include "mapview.h"
46#include "sprite.h"
47
48#include "themespec.h"
49
50#define THEMESPEC_SDL2_CAPSTR "+Freeciv-sdl2-3.3-themespec-Devel-2023-Jun-07 duplicates_ok"
51/*
52 * Themespec capabilities acceptable to this program:
53 *
54 * +Freeciv-3.3-sdl2-themespec - basic format for Freeciv versions 3.3.x;
55 * required
56 *
57 * duplicates_ok - we can handle existence of duplicate tags
58 * (lattermost tag which appears is used; themes which
59 * have duplicates should specify "+duplicates_ok")
60 */
61
62#define SPEC_SDL2_CAPSTR "+Freeciv-sdl2-3.3-spec-Devel-2023-Jun-10"
63/*
64 * Individual spec file capabilities acceptable to this program:
65 *
66 * +Freeciv-3.3-sdl2-spec - basic format for Freeciv versions 3.3.x; required
67 */
68
69#define THEMESPEC_SUFFIX ".themespec"
70
71#if 0
72/* TODO: may be useful for theme sprites, too */
73struct named_sprites {
74 ....
75};
76#endif /* 0 */
77
78 struct specfile {
79 struct sprite *big_sprite;
80 char *file_name;
81};
82
83#define SPECLIST_TAG specfile
84#define SPECLIST_TYPE struct specfile
85#include "speclist.h"
86
87#define specfile_list_iterate(list, pitem) \
88 TYPED_LIST_ITERATE(struct specfile, list, pitem)
89#define specfile_list_iterate_end LIST_ITERATE_END
90
91/*
92 * Information about an individual sprite. All fields except 'sprite' are
93 * filled at the time of the scan of the specfile. 'Sprite' is
94 * set/cleared on demand in theme_load_sprite/theme_unload_sprite.
95 */
96 struct small_sprite {
97 int ref_count;
98
99 /* The sprite is in this file. */
100 char *file;
101
102 /* Or, the sprite is in this file at the location. */
103 struct specfile *sf;
104 int x, y, width, height;
105
106 /* A little more (optional) data. */
107 int hot_x, hot_y;
108
109 struct sprite *sprite;
110};
111
112#define SPECLIST_TAG small_sprite
113#define SPECLIST_TYPE struct small_sprite
114#include "speclist.h"
115
116#define small_sprite_list_iterate(list, pitem) \
117 TYPED_LIST_ITERATE(struct small_sprite, list, pitem)
118#define small_sprite_list_iterate_end LIST_ITERATE_END
119
120#define SPECHASH_TAG small_sprite
121#define SPECHASH_IKEY_TYPE char *
122#define SPECHASH_IDATA_TYPE struct small_sprite *
123#define SPECHASH_IKEY_VAL genhash_str_val_func
124#define SPECHASH_IKEY_COMP genhash_str_comp_func
125#define SPECHASH_IKEY_COPY genhash_str_copy_func
126#define SPECHASH_IKEY_FREE genhash_str_free_func
127#include "spechash.h"
128#define small_sprite_hash_iterate(hash, tag_name, pitem) \
129 TYPED_HASH_ITERATE(const char *, struct small_sprite *, hash, \
130 tag_name, pitem)
131#define small_sprite_hash_iterate_end HASH_ITERATE_END
132
133 struct theme {
134 char name[512];
135 int priority;
136
137 char *font_filename;
138 int default_font_size;
139
140 struct specfile_list *specfiles;
141 struct small_sprite_list *small_sprites;
142
143 /*
144 * This hash table maps themespec tags to struct small_sprites.
145 */
146 struct small_sprite_hash *sprite_hash;
147
148/* struct named_sprites sprites;*/
149
150 struct theme_background_system *background_system;
151 struct theme_color_system *color_system;
152};
153
154 struct theme *active_theme = NULL;
155
156
157/************************************************************************/
160 const char *theme_get_name(const struct theme *t)
161{
162 return t->name;
163}
164
165/************************************************************************/
169 const char *theme_font_filename(const struct theme *t)
170{
171 return t->font_filename;
172}
173
174/************************************************************************/
177 int theme_default_font_size(const struct theme *t)
178{
179 return t->default_font_size;
180}
181
182/************************************************************************/
185 static struct theme *theme_new(void)
186{
187 struct theme *t = fc_calloc(1, sizeof(*t));
188
189 t->specfiles = specfile_list_new();
190 t->small_sprites = small_sprite_list_new();
191
192 return t;
193}
194
195/************************************************************************/
200 const char **get_theme_list(void)
201{
202 static const char **themes = NULL;
203
204 if (!themes) {
205 /* Note: this means you must restart the client after installing a new
206 theme. */
207 struct strvec *list = fileinfolist(get_data_dirs(), THEMESPEC_SUFFIX);
208 int count = 0;
209
210 themes = fc_malloc((1 + strvec_size(list)) * sizeof(*themes));
211 strvec_iterate(list, file) {
212 struct theme *t = theme_read_toplevel(file);
213
214 if (t) {
215 themes[count++] = fc_strdup(file);
216 theme_free(t);
217 }
218 } strvec_iterate_end;
219 themes[count] = NULL;
220 strvec_destroy(list);
221 }
222
223 return themes;
224}
225
226/************************************************************************/
233 static char *themespec_fullname(const char *theme_name)
234{
235 if (theme_name) {
236 char fname[strlen(theme_name) + strlen(THEMESPEC_SUFFIX) + 1], *dname;
237
238 fc_snprintf(fname, sizeof(fname),
239 "%s%s", theme_name, THEMESPEC_SUFFIX);
240
241 dname = fname;
242
243 if (dname) {
244 return fc_strdup(dname);
245 }
246 }
247
248 return NULL;
249}
250
251/************************************************************************/
256 static bool check_themespec_capabilities(struct section_file *file,
257 const char *which,
258 const char *us_capstr,
259 const char *filename)
260{
261 const char *file_capstr = secfile_lookup_str(file, "%s.options", which);
262
263 if (NULL == file_capstr) {
264 log_debug("\"%s\": %s file doesn't have capability string",
265 filename, which);
266 return FALSE;
267 }
268
269 if (!has_capabilities(us_capstr, file_capstr)) {
270 log_debug("\"%s\": %s file appears incompatible:", filename, which);
271 log_debug(" datafile options: %s", file_capstr);
272 log_debug(" supported options: %s", us_capstr);
273
274 return FALSE;
275 }
276
277 if (!has_capabilities(file_capstr, us_capstr)) {
278 log_debug("\"%s\": %s file requires option(s)"
279 " that client doesn't support:", filename, which);
280 log_debug(" datafile options: %s", file_capstr);
281 log_debug(" supported options: %s", us_capstr);
282
283 return FALSE;
284 }
285
286 return TRUE;
287}
288
289/************************************************************************/
294 static void theme_free_toplevel(struct theme *t)
295{
296 if (t->font_filename) {
297 free(t->font_filename);
298 t->font_filename = NULL;
299 }
300
301 t->default_font_size = 0;
302
303 if (t->background_system) {
304 theme_background_system_free(t->background_system);
305 t->background_system = NULL;
306 }
307
308 if (t->color_system) {
309 theme_color_system_free(t->color_system);
310 t->color_system = NULL;
311 }
312}
313
314/************************************************************************/
317 void theme_free(struct theme *ftheme)
318{
319 if (ftheme != NULL) {
320 theme_free_sprites(ftheme);
321 theme_free_toplevel(ftheme);
322 specfile_list_destroy(ftheme->specfiles);
323 small_sprite_list_destroy(ftheme->small_sprites);
324 free(ftheme);
325 }
326}
327
328/************************************************************************/
334 void themespec_try_read(const char *theme_name)
335{
336 if (!(active_theme = theme_read_toplevel(theme_name))) {
337 struct strvec *list = fileinfolist(get_data_dirs(), THEMESPEC_SUFFIX);
338
339 strvec_iterate(list, file) {
340 struct theme *t = theme_read_toplevel(file);
341
342 if (t) {
343 if (active_theme == NULL || t->priority > active_theme->priority) {
344 active_theme = t;
345 } else {
346 theme_free(t);
347 }
348 }
349 } strvec_iterate_end;
350 strvec_destroy(list);
351
352 if (active_theme == NULL) {
353 log_fatal(_("No usable default theme found, aborting!"));
354
355 exit(EXIT_FAILURE);
356 }
357
358 log_verbose("Trying theme \"%s\".", active_theme->name);
359 }
360
361 /* sz_strlcpy(GUI_SDL_OPTION(default_theme_name),
362 theme_get_name(theme)); */
363}
364
365/************************************************************************/
376 void themespec_reread(const char *new_theme_name)
377{
378 struct tile *center_tile;
379 enum client_states state = client_state();
380 const char *name = new_theme_name ? new_theme_name : active_theme->name;
381 char theme_name[strlen(name) + 1], old_name[strlen(active_theme->name) + 1];
382
383 /* Make local copies since these values may be freed down below */
384 sz_strlcpy(theme_name, name);
385 sz_strlcpy(old_name, active_theme->name);
386
387 log_normal(_("Loading theme \"%s\"."), theme_name);
388
389 /* Step 0: Record old data.
390 *
391 * We record the current mapcanvas center, etc.
392 */
393 center_tile = get_center_tile_mapcanvas();
394
395 /* Step 1: Cleanup.
396 *
397 * We free all old data in preparation for re-reading it.
398 */
399 theme_free_sprites(active_theme);
400 theme_free_toplevel(active_theme);
401
402 /* Step 2: Read.
403 *
404 * We read in the new theme. This should be pretty straightforward.
405 */
406 if (!(active_theme = theme_read_toplevel(theme_name))) {
407 if (!(active_theme = theme_read_toplevel(old_name))) {
408 /* Always fails. */
409 fc_assert_exit_msg(NULL != active_theme,
410 "Failed to re-read the currently loaded theme.");
411 }
412 }
413 /* sz_strlcpy(GUI_SDL_OPTION(default_theme_name), theme->name); */
414
415 theme_load_sprites(active_theme);
416
417 /* Step 3: Setup
418 *
419 * This is a seriously sticky problem. On startup, we build a hash
420 * from all the sprite data. Then, when we connect to a server, the
421 * server sends us ruleset data a piece at a time and we use this data
422 * to assemble the sprite structures. But if we change while connected
423 * we have to reassemble all of these. This should just involve
424 * calling themespec_setup_*** on everything. But how do we tell what
425 * "everything" is?
426 *
427 * The below code just does things straightforwardly, by setting up
428 * each possible sprite again. Hopefully it catches everything, and
429 * doesn't mess up too badly if we change themes while not connected
430 * to a server.
431 */
432 if (state < C_S_RUNNING) {
433 /* The ruleset data is not sent until this point. */
434 return;
435 }
436
437 /* Step 4: Draw.
438 *
439 * Do any necessary redraws.
440 */
441 popdown_all_game_dialogs();
442 generate_citydlg_dimensions();
443/* theme_changed();*/
444 can_slide = FALSE;
445 center_tile_mapcanvas(center_tile);
446 /* update_map_canvas_visible() forces a full redraw. Otherwise with fast
447 * drawing we might not get one. Of course this is slower. */
448 update_map_canvas_visible();
449 can_slide = TRUE;
450}
451
452/************************************************************************/
456 static struct sprite *load_sdl2_gfx_file(const char *gfx_filename)
457{
458 const char **gfx_fileexts = gfx_fileextensions(), *gfx_fileext;
459 struct sprite *s;
460
461 /* Try out all supported file extensions to find one that works. */
462 while ((gfx_fileext = *gfx_fileexts++)) {
463 const char *real_full_name;
464 char full_name[strlen(gfx_filename) + strlen(".")
465 + strlen(gfx_fileext) + 1];
466
467 sprintf(full_name, "%s.%s", gfx_filename, gfx_fileext);
468 if ((real_full_name = fileinfoname(get_data_dirs(), full_name))) {
469 log_debug("trying to load gfx file \"%s\".", real_full_name);
470 s = load_gfxfile(real_full_name, FALSE);
471 if (s) {
472 return s;
473 }
474 }
475 }
476
477 log_error("Could not load gfx file \"%s\".", gfx_filename);
478
479 return NULL;
480}
481
482/************************************************************************/
485 static void theme_ensure_big_sprite(struct specfile *sf)
486{
487 struct section_file *file;
488 const char *gfx_filename;
489
490 if (sf->big_sprite) {
491 /* Looks like it's already loaded. */
492 return;
493 }
494
495 /* Otherwise load it. The big sprite will sometimes be freed and will have
496 * to be reloaded, but most of the time it's just loaded once, the small
497 * sprites are extracted, and then it's freed. */
498 if (!(file = secfile_load(sf->file_name, TRUE))) {
499 log_fatal(_("Could not open '%s':\n%s"), sf->file_name, secfile_error());
500 exit(EXIT_FAILURE);
501 }
502
503 if (!check_themespec_capabilities(file, "spec",
504 SPEC_SDL2_CAPSTR, sf->file_name)) {
505 exit(EXIT_FAILURE);
506 }
507
508 gfx_filename = secfile_lookup_str(file, "file.gfx");
509
510 sf->big_sprite = load_sdl2_gfx_file(gfx_filename);
511
512 if (!sf->big_sprite) {
513 log_fatal("Could not load gfx file for the spec file \"%s\".",
514 sf->file_name);
515 exit(EXIT_FAILURE);
516 }
517
518 /* We don't check unused fields here as most fields are only read
519 * at the specs scanning time. */
520 /* secfile_check_unused(file); */
521
522 secfile_destroy(file);
523}
524
525/************************************************************************/
530 static void scan_specfile(struct theme *t, struct specfile *sf,
531 bool duplicates_ok)
532{
533 struct section_file *file;
534 struct section_list *sections;
535 int i;
536
537 if (!(file = secfile_load(sf->file_name, TRUE))) {
538 log_fatal(_("Could not open '%s':\n%s"), sf->file_name, secfile_error());
539 exit(EXIT_FAILURE);
540 }
541 if (!check_themespec_capabilities(file, "spec",
542 SPEC_SDL2_CAPSTR, sf->file_name)) {
543 exit(EXIT_FAILURE);
544 }
545
546 /* Currently unused */
547 (void) secfile_entry_by_path(file, "info.artists");
548
549 /* This isn't used during the scan, only when the gfx is really loaded */
550 (void) secfile_entry_by_path(file, "file.gfx");
551
552 sections = secfile_sections_by_name_prefix(file, "grid_");
553
554 if (NULL != sections) {
555 section_list_iterate(sections, psection) {
556 int j, k;
557 int x_top_left, y_top_left, dx, dy;
558 int pixel_border;
559 const char *gridname = section_name(psection);
560
561 pixel_border = secfile_lookup_int_default(file, 0, "%s.pixel_border",
562 gridname);
563
564 if (!secfile_lookup_int(file, &x_top_left, "%s.x_top_left", gridname)
565 || !secfile_lookup_int(file, &y_top_left,
566 "%s.y_top_left", gridname)
567 || !secfile_lookup_int(file, &dx, "%s.dx", gridname)
568 || !secfile_lookup_int(file, &dy, "%s.dy", gridname)) {
569 log_error("Grid \"%s\" invalid: %s", gridname, secfile_error());
570 continue;
571 }
572
573 j = -1;
574 while (NULL != secfile_entry_lookup(file, "%s.tiles%d.tag",
575 gridname, ++j)) {
576 struct small_sprite *ss;
577 int row, column;
578 int x1, y1;
579 const char **tags;
580 size_t num_tags;
581 int hot_x, hot_y;
582
583 if (!secfile_lookup_int(file, &row, "%s.tiles%d.row", gridname, j)
584 || !secfile_lookup_int(file, &column,
585 "%s.tiles%d.column", gridname, j)
586 || !(tags = secfile_lookup_str_vec(file, &num_tags,
587 "%s.tiles%d.tag",
588 gridname, j))) {
589 log_error("Small sprite \"%s.tiles%d\" invalid: %s",
590 gridname, j, secfile_error());
591 continue;
592 }
593 hot_x = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_x",
594 gridname, j);
595 hot_y = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_y",
596 gridname, j);
597
598 /* there must be at least 1 because of the while(): */
599 fc_assert_action(num_tags > 0, continue);
600
601 x1 = x_top_left + (dx + pixel_border) * column;
602 y1 = y_top_left + (dy + pixel_border) * row;
603
604 ss = fc_malloc(sizeof(*ss));
605 ss->ref_count = 0;
606 ss->file = NULL;
607 ss->x = x1;
608 ss->y = y1;
609 ss->width = dx;
610 ss->height = dy;
611 ss->sf = sf;
612 ss->sprite = NULL;
613 ss->hot_x = hot_x;
614 ss->hot_y = hot_y;
615
616 small_sprite_list_prepend(t->small_sprites, ss);
617
618 if (!duplicates_ok) {
619 for (k = 0; k < num_tags; k++) {
620 if (!small_sprite_hash_insert(t->sprite_hash, tags[k], ss)) {
621 log_error("warning: already have a sprite for \"%s\".",
622 tags[k]);
623 }
624 }
625 } else {
626 for (k = 0; k < num_tags; k++) {
627 (void) small_sprite_hash_replace(t->sprite_hash, tags[k], ss);
628 }
629 }
630
631 FC_FREE(tags);
632 }
633 } section_list_iterate_end;
634 section_list_destroy(sections);
635 }
636
637 /* Load "extra" sprites. Each sprite is one file. */
638 i = -1;
639 while (NULL != secfile_lookup_str(file, "extra.sprites%d.tag", ++i)) {
640 struct small_sprite *ss;
641 const char **tags;
642 const char *filename;
643 size_t num_tags, k;
644 int hot_x, hot_y;
645
646 if (!(tags = secfile_lookup_str_vec(file, &num_tags,
647 "extra.sprites%d.tag", i))
648 || !(filename = secfile_lookup_str(file,
649 "extra.sprites%d.file", i))) {
650 log_error("Small sprite \"extra.sprites%d\" invalid: %s",
651 i, secfile_error());
652 continue;
653 }
654
655 hot_x = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_x", i);
656 hot_y = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_y", i);
657
658 ss = fc_malloc(sizeof(*ss));
659 ss->ref_count = 0;
660 ss->file = fc_strdup(filename);
661 ss->sf = NULL;
662 ss->sprite = NULL;
663 ss->hot_x = hot_x;
664 ss->hot_y = hot_y;
665
666 small_sprite_list_prepend(t->small_sprites, ss);
667
668 if (!duplicates_ok) {
669 for (k = 0; k < num_tags; k++) {
670 if (!small_sprite_hash_insert(t->sprite_hash, tags[k], ss)) {
671 log_error("warning: already have a sprite for \"%s\".", tags[k]);
672 }
673 }
674 } else {
675 for (k = 0; k < num_tags; k++) {
676 (void) small_sprite_hash_replace(t->sprite_hash, tags[k], ss);
677 }
678 }
679 FC_FREE(tags);
680 }
681
682 secfile_check_unused(file);
683 secfile_destroy(file);
684}
685
686/************************************************************************/
690 char *themespec_gfx_filename(const char *gfx_filename)
691{
692 const char *gfx_current_fileext;
693 const char **gfx_fileexts = gfx_fileextensions();
694
695 while ((gfx_current_fileext = *gfx_fileexts++)) {
696 const char *real_full_name;
697 char *full_name =
698 fc_malloc(strlen(gfx_filename) + strlen(".")
699 + strlen(gfx_current_fileext) + 1);
700
701 sprintf(full_name, "%s.%s", gfx_filename, gfx_current_fileext);
702 real_full_name = fileinfoname(get_data_dirs(), full_name);
703 FC_FREE(full_name);
704 if (real_full_name) {
705 return fc_strdup(real_full_name);
706 }
707 }
708
709 log_fatal("Couldn't find a supported gfx file extension for \"%s\".",
710 gfx_filename);
711
712 exit(EXIT_FAILURE);
713 return NULL;
714}
715
716/************************************************************************/
721 struct theme *theme_read_toplevel(const char *theme_name)
722{
723 struct section_file *file;
724 char *fname;
725 int i;
726 size_t num_spec_files;
727 const char **spec_filenames;
728 const char *file_capstr;
729 bool duplicates_ok;
730 struct theme *t = theme_new();
731 const char *langname;
732 const char *filename, *t_fontname;
733 const char *lang;
734
735 fname = themespec_fullname(theme_name);
736 if (!fname) {
737 log_error("Can't find theme \"%s\".", theme_name);
738 theme_free(t);
739 return NULL;
740 }
741 log_verbose("themespec file is \"%s\".", fname);
742
743 if (!(file = secfile_load(fname, TRUE))) {
744 log_error("Could not open '%s':\n%s", fname, secfile_error());
745 FC_FREE(fname);
746 theme_free(t);
747
748 return NULL;
749 }
750
751 if (!check_themespec_capabilities(file, "themespec",
752 THEMESPEC_SDL2_CAPSTR, fname)) {
753 secfile_destroy(file);
754 FC_FREE(fname);
755 theme_free(t);
756
757 return NULL;
758 }
759
760 file_capstr = secfile_lookup_str(file, "themespec.options");
761 duplicates_ok = has_capabilities("+duplicates_ok", file_capstr);
762
763 (void) secfile_entry_by_path(file, "themespec.name"); /* Currently unused */
764
765 sz_strlcpy(t->name, theme_name);
766 t->priority = secfile_lookup_int_default(file, 0, "themespec.priority");
767
768 langname = setup_langname();
769
770 t_fontname = NULL;
771 for (i = 0;
772 (lang = secfile_lookup_str_default(file, NULL, "themespec.fonts%d.langname", i)) != NULL;
773 i++) {
774 if (langname != NULL && strstr(langname, lang) != NULL) {
775 t_fontname = secfile_lookup_str(file, "themespec.fonts%d.font_file", i);
776 /* Don't break the loop, but read all the entries to avoid "unused entry" warnings */
777 } else {
778 (void) secfile_entry_lookup(file, "themespec.fonts%d.font_file", i);
779 }
780 }
781
782 if (t_fontname == NULL) {
783 t_fontname = secfile_lookup_str(file, "themespec.default_font_file");
784 } else {
785 (void) secfile_entry_lookup(file, "themespec.default_font_file");
786 }
787
788 if ((filename = fileinfoname(get_data_dirs(), t_fontname))) {
789 t->font_filename = fc_strdup(filename);
790 } else {
791 log_fatal("Could not open font: %s", t_fontname);
792 secfile_destroy(file);
793 FC_FREE(fname);
794 theme_free(t);
795
796 return NULL;
797 }
798 log_debug("theme font file %s", t->font_filename);
799
800 t->default_font_size = secfile_lookup_int_default(file, 10, "themespec.default_font_size");
801 log_debug("theme default font size %d", t->default_font_size);
802
803 spec_filenames = secfile_lookup_str_vec(file, &num_spec_files,
804 "themespec.files");
805 if (NULL == spec_filenames || 0 == num_spec_files) {
806 log_error("No theme graphics files specified in \"%s\"", fname);
807 secfile_destroy(file);
808 FC_FREE(fname);
809 theme_free(t);
810
811 return NULL;
812 }
813
814 fc_assert(t->sprite_hash == NULL);
815 t->sprite_hash = small_sprite_hash_new();
816 for (i = 0; i < num_spec_files; i++) {
817 struct specfile *sf = fc_malloc(sizeof(*sf));
818
819 log_debug("spec file %s", spec_filenames[i]);
820
821 sf->big_sprite = NULL;
822 filename = fileinfoname(get_data_dirs(), spec_filenames[i]);
823 if (!filename) {
824 log_error("Can't find spec file \"%s\".", spec_filenames[i]);
825 secfile_destroy(file);
826 FC_FREE(fname);
827 theme_free(t);
828
829 return NULL;
830 }
831 sf->file_name = fc_strdup(filename);
832 scan_specfile(t, sf, duplicates_ok);
833
834 specfile_list_prepend(t->specfiles, sf);
835 }
836 FC_FREE(spec_filenames);
837
838 t->background_system = theme_background_system_read(file);
839 t->color_system = theme_color_system_read(file);
840
841 secfile_check_unused(file);
842
843 secfile_destroy(file);
844 log_verbose("finished reading \"%s\".", fname);
845 FC_FREE(fname);
846
847 return t;
848}
849
850/************************************************************************/
855 static struct sprite *theme_load_sprite(struct theme *t, const char *tag_name)
856{
857 /* Lookup information about where the sprite is found. */
858 struct small_sprite *ss;
859
860 log_debug("theme_load_sprite(tag='%s')", tag_name);
861 if (!small_sprite_hash_lookup(t->sprite_hash, tag_name, &ss)) {
862 return NULL;
863 }
864
865 fc_assert_ret_val(ss->ref_count >= 0, NULL);
866
867 if (!ss->sprite) {
868 /* If the sprite hasn't been loaded already, then load it. */
869 fc_assert_ret_val(ss->ref_count == 0, NULL);
870 if (ss->file) {
871 ss->sprite = load_sdl2_gfx_file(ss->file);
872 if (!ss->sprite) {
873 log_fatal("Couldn't load gfx file \"%s\" for sprite '%s'.",
874 ss->file, tag_name);
875 exit(EXIT_FAILURE);
876 }
877 } else {
878 int sf_w, sf_h;
879
880 theme_ensure_big_sprite(ss->sf);
881 get_sprite_dimensions(ss->sf->big_sprite, &sf_w, &sf_h);
882 if (ss->x < 0 || ss->x + ss->width > sf_w
883 || ss->y < 0 || ss->y + ss->height > sf_h) {
884 log_error("Sprite '%s' in file \"%s\" isn't within the image!",
885 tag_name, ss->sf->file_name);
886 return NULL;
887 }
888 ss->sprite =
889 crop_sprite(ss->sf->big_sprite, ss->x, ss->y, ss->width, ss->height,
890 NULL, -1, -1, 1.0, FALSE);
891 }
892 }
893
894 /* Track the reference count so we know when to free the sprite. */
895 ss->ref_count++;
896
897 return ss->sprite;
898}
899
900/************************************************************************/
904 static void theme_unload_sprite(struct theme *t, const char *tag_name)
905{
906 struct small_sprite *ss;
907
908 small_sprite_hash_lookup(t->sprite_hash, tag_name, &ss);
909 fc_assert_ret(ss);
910 fc_assert_ret(ss->ref_count >= 1);
911 fc_assert_ret(ss->sprite);
912
913 ss->ref_count--;
914
915 if (ss->ref_count == 0) {
916 /* Nobody's using the sprite anymore, so we should free it. We know
917 * where to find it if we need it again. */
918 log_debug("freeing sprite '%s'.", tag_name);
919 free_sprite(ss->sprite);
920 ss->sprite = NULL;
921 }
922}
923
924/* Not very safe, but convenient: */
925#define SET_SPRITE(field, tag) \
926 do { \
927 t->sprites.field = theme_load_sprite(t, tag); \
928 fc_assert_exit_msg(NULL != t->sprites.field, \
929 "Sprite tag '%s' missing.", tag); \
930 } while (FALSE)
931
932/* Sets sprites.field to tag or (if tag isn't available) to alt */
933#define SET_SPRITE_ALT(field, tag, alt) \
934 do { \
935 t->sprites.field = theme_load_sprite(t, tag); \
936 if (!t->sprites.field) { \
937 t->sprites.field = theme_load_sprite(t, alt); \
938 } \
939 fc_assert_exit_msg(NULL != t->sprites.field, \
940 "Sprite tag '%s' and alternate '%s' are " \
941 "both missing.", tag, alt); \
942 } while (FALSE)
943
944/* Sets sprites.field to tag, or NULL if not available */
945#define SET_SPRITE_OPT(field, tag) \
946 t->sprites.field = theme_load_sprite(t, tag)
947
948#define SET_SPRITE_ALT_OPT(field, tag, alt) \
949 do { \
950 t->sprites.field = theme_lookup_sprite_tag_alt(t, LOG_VERBOSE, tag, alt,\
951 "sprite", #field); \
952 } while (FALSE)
953
954/************************************************************************/
958 static void theme_lookup_sprite_tags(struct theme *t)
959{
960 /* the 'sprites' structure is currently not used, for now we call some
961 * functions in gui_tilespec.c instead */
962
963 tilespec_setup_theme();
964 tilespec_setup_city_gfx();
965 tilespec_setup_city_icons();
966}
967
968/************************************************************************/
974 static void theme_finish_loading_sprites(struct theme *t)
975{
976 specfile_list_iterate(t->specfiles, sf) {
977 if (sf->big_sprite) {
978 free_sprite(sf->big_sprite);
979 sf->big_sprite = NULL;
980 }
981 } specfile_list_iterate_end;
982}
983
984/************************************************************************/
990 void theme_load_sprites(struct theme *t)
991{
992 theme_lookup_sprite_tags(t);
993 theme_finish_loading_sprites(t);
994}
995
996/************************************************************************/
1000 struct sprite *theme_lookup_sprite_tag_alt(struct theme *t,
1001 enum log_level level,
1002 const char *tag, const char *alt,
1003 const char *what,
1004 const char *name)
1005{
1006 struct sprite *sp;
1007
1008 /* (should get sprite_hash before connection) */
1009 fc_assert_ret_val_msg(NULL != t->sprite_hash, NULL,
1010 "attempt to lookup for %s \"%s\" before "
1011 "sprite_hash setup", what, name);
1012
1013 sp = theme_load_sprite(t, tag);
1014 if (sp) {
1015 return sp;
1016 }
1017
1018 sp = theme_load_sprite(t, alt);
1019 if (sp) {
1020 log_verbose("Using alternate graphic \"%s\" (instead of \"%s\") "
1021 "for %s \"%s\".", alt, tag, what, name);
1022 return sp;
1023 }
1024
1025 log_base(level, "Don't have graphics tags \"%s\" or \"%s\" for %s \"%s\".",
1026 tag, alt, what, name);
1027 if (LOG_FATAL >= level) {
1028 exit(EXIT_FAILURE);
1029 }
1030
1031 return NULL;
1032}
1033
1034#define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2)
1035#define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height)
1036
1037#define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \
1038 (fc_assert(s != NULL), \
1039 sprs->sprite = s, \
1040 sprs->foggable = (draw_fog && t->fogstyle == FOG_AUTO), \
1041 sprs->offset_x = x_offset, \
1042 sprs->offset_y = y_offset, \
1043 sprs++)
1044#define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, TRUE, 0, 0)
1045#define ADD_SPRITE_FULL(s) \
1046 ADD_SPRITE(s, TRUE, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET)
1047
1048/************************************************************************/
1051 static void theme_unload_all_sprites(struct theme *t)
1052{
1053 if (t->sprite_hash) {
1054 small_sprite_hash_iterate(t->sprite_hash, tag_name, ss) {
1055 while (ss->ref_count > 0) {
1056 theme_unload_sprite(t, tag_name);
1057 }
1058 } small_sprite_hash_iterate_end;
1059 }
1060}
1061
1062/************************************************************************/
1065 void theme_free_sprites(struct theme *t)
1066{
1067 log_debug("theme_free_sprites()");
1068
1069 theme_unload_all_sprites(t);
1070
1071 if (t->sprite_hash) {
1072 small_sprite_hash_destroy(t->sprite_hash);
1073 t->sprite_hash = NULL;
1074 }
1075
1076 small_sprite_list_iterate(t->small_sprites, ss) {
1077 small_sprite_list_remove(t->small_sprites, ss);
1078 if (ss->file) {
1079 FC_FREE(ss->file);
1080 }
1081 fc_assert(ss->sprite == NULL);
1082 FC_FREE(ss);
1083 } small_sprite_list_iterate_end;
1084
1085 specfile_list_iterate(t->specfiles, sf) {
1086 specfile_list_remove(t->specfiles, sf);
1087 FC_FREE(sf->file_name);
1088 if (sf->big_sprite) {
1089 free_sprite(sf->big_sprite);
1090 sf->big_sprite = NULL;
1091 }
1092 FC_FREE(sf);
1093 } specfile_list_iterate_end;
1094
1095#if 0
1096 /* TODO: may be useful for theme sprites, too */
1097 sprite_vector_iterate(&t->sprites.****, psprite) {
1098 free_sprite(*psprite);
1099 } sprite_vector_iterate_end;
1100 sprite_vector_free(&t->sprites.****);
1101#endif /* 0 */
1102
1103 /* the 'sprites' structure is currently not used, for now we call some
1104 * functions in gui_tilespec.c instead */
1105
1106 tilespec_free_theme();
1107 tilespec_free_city_gfx();
1108 tilespec_free_city_icons();
1109}
1110
1111/************************************************************************/
1114 struct theme_background_system *theme_get_background_system(const struct theme *t)
1115{
1116 return t->background_system;
1117}
1118
1119/************************************************************************/
1122 struct theme_color_system *theme_get_color_system(const struct theme *t)
1123{
1124 return t->color_system;
1125}
has_capabilities
bool has_capabilities(const char *us, const char *them)
Definition capability.c:86
capability.h
generate_citydlg_dimensions
void generate_citydlg_dimensions(void)
Definition citydlg_common.c:67
citydlg_common.h
client_state
enum client_states client_state(void)
Definition client_main.c:1026
client_main.h
client_states
client_states
Definition client_main.h:43
C_S_RUNNING
@ C_S_RUNNING
Definition client_main.h:47
list
static struct fc_sockaddr_list * list
Definition clinet.c:102
incite_cost
char * incite_cost
Definition comments.c:74
tag_name
char * tag_name
Definition events.c:77
fcintl.h
_
#define _(String)
Definition fcintl.h:67
game.h
popdown_all_game_dialogs
void popdown_all_game_dialogs(void)
Definition dialogs.c:1514
free_sprite
void free_sprite(struct sprite *s)
Definition sprite.c:278
load_gfxfile
struct sprite * load_gfxfile(const char *filename, bool svgflag)
Definition sprite.c:170
gfx_fileextensions
const char ** gfx_fileextensions(void)
Definition sprite.c:117
tilespec_setup_city_gfx
void tilespec_setup_city_gfx(void)
Definition gui_tilespec.c:135
tilespec_free_city_icons
void tilespec_free_city_icons(void)
Definition gui_tilespec.c:227
tilespec_setup_theme
void tilespec_setup_theme(void)
Definition gui_tilespec.c:261
tilespec_free_city_gfx
void tilespec_free_city_gfx(void)
Definition gui_tilespec.c:149
tilespec_setup_city_icons
void tilespec_setup_city_icons(void)
Definition gui_tilespec.c:160
tilespec_free_theme
void tilespec_free_theme(void)
Definition gui_tilespec.c:366
theme_background_system_read
struct theme_background_system * theme_background_system_read(struct section_file *file)
Definition themebackgrounds.c:61
theme_background_system_free
void theme_background_system_free(struct theme_background_system *backgrounds)
Definition themebackgrounds.c:80
theme_color_system_free
void theme_color_system_free(struct theme_color_system *colors)
Definition themecolors.c:175
theme_color_system_read
struct theme_color_system * theme_color_system_read(struct section_file *file)
Definition themecolors.c:151
theme_load_sprite
static struct sprite * theme_load_sprite(struct theme *t, const char *tag_name)
Definition themespec.c:855
theme_free
void theme_free(struct theme *ftheme)
Definition themespec.c:317
check_themespec_capabilities
static bool check_themespec_capabilities(struct section_file *file, const char *which, const char *us_capstr, const char *filename)
Definition themespec.c:256
THEMESPEC_SDL2_CAPSTR
#define THEMESPEC_SDL2_CAPSTR
Definition themespec.c:50
specfile_list_iterate_end
#define specfile_list_iterate_end
Definition themespec.c:89
theme_unload_sprite
static void theme_unload_sprite(struct theme *t, const char *tag_name)
Definition themespec.c:904
theme_get_color_system
struct theme_color_system * theme_get_color_system(const struct theme *t)
Definition themespec.c:1122
theme_font_filename
const char * theme_font_filename(const struct theme *t)
Definition themespec.c:169
themespec_gfx_filename
char * themespec_gfx_filename(const char *gfx_filename)
Definition themespec.c:690
specfile_list_iterate
#define specfile_list_iterate(list, pitem)
Definition themespec.c:87
theme_finish_loading_sprites
static void theme_finish_loading_sprites(struct theme *t)
Definition themespec.c:974
theme_ensure_big_sprite
static void theme_ensure_big_sprite(struct specfile *sf)
Definition themespec.c:485
theme_lookup_sprite_tags
static void theme_lookup_sprite_tags(struct theme *t)
Definition themespec.c:958
small_sprite_list_iterate_end
#define small_sprite_list_iterate_end
Definition themespec.c:118
small_sprite_list_iterate
#define small_sprite_list_iterate(list, pitem)
Definition themespec.c:116
theme_free_toplevel
static void theme_free_toplevel(struct theme *t)
Definition themespec.c:294
scan_specfile
static void scan_specfile(struct theme *t, struct specfile *sf, bool duplicates_ok)
Definition themespec.c:530
theme_free_sprites
void theme_free_sprites(struct theme *t)
Definition themespec.c:1065
theme_load_sprites
void theme_load_sprites(struct theme *t)
Definition themespec.c:990
get_theme_list
const char ** get_theme_list(void)
Definition themespec.c:200
theme_get_background_system
struct theme_background_system * theme_get_background_system(const struct theme *t)
Definition themespec.c:1114
themespec_try_read
void themespec_try_read(const char *theme_name)
Definition themespec.c:334
theme_lookup_sprite_tag_alt
struct sprite * theme_lookup_sprite_tag_alt(struct theme *t, enum log_level level, const char *tag, const char *alt, const char *what, const char *name)
Definition themespec.c:1000
theme_get_name
const char * theme_get_name(const struct theme *t)
Definition themespec.c:160
theme_read_toplevel
struct theme * theme_read_toplevel(const char *theme_name)
Definition themespec.c:721
small_sprite_hash_iterate
#define small_sprite_hash_iterate(hash, tag_name, pitem)
Definition themespec.c:128
SPEC_SDL2_CAPSTR
#define SPEC_SDL2_CAPSTR
Definition themespec.c:62
THEMESPEC_SUFFIX
#define THEMESPEC_SUFFIX
Definition themespec.c:69
theme_default_font_size
int theme_default_font_size(const struct theme *t)
Definition themespec.c:177
small_sprite_hash_iterate_end
#define small_sprite_hash_iterate_end
Definition themespec.c:131
themespec_reread
void themespec_reread(const char *new_theme_name)
Definition themespec.c:376
active_theme
struct theme * active_theme
Definition themespec.c:154
themespec_fullname
static char * themespec_fullname(const char *theme_name)
Definition themespec.c:233
theme_unload_all_sprites
static void theme_unload_all_sprites(struct theme *t)
Definition themespec.c:1051
theme_new
static struct theme * theme_new(void)
Definition themespec.c:185
load_sdl2_gfx_file
static struct sprite * load_sdl2_gfx_file(const char *gfx_filename)
Definition themespec.c:456
gui_tilespec.h
themespec.h
dialogs.h
mapview.h
sprite.h
name
const char * name
Definition inputfile.c:127
log.h
fc_assert_ret
#define fc_assert_ret(condition)
Definition log.h:191
fc_assert_exit_msg
#define fc_assert_exit_msg(condition, message,...)
Definition log.h:211
log_verbose
#define log_verbose(message,...)
Definition log.h:109
fc_assert
#define fc_assert(condition)
Definition log.h:176
fc_assert_ret_val
#define fc_assert_ret_val(condition, val)
Definition log.h:194
log_fatal
#define log_fatal(message,...)
Definition log.h:100
fc_assert_action
#define fc_assert_action(condition, action)
Definition log.h:187
log_debug
#define log_debug(message,...)
Definition log.h:115
log_normal
#define log_normal(message,...)
Definition log.h:107
log_base
#define log_base(level, message,...)
Definition log.h:94
log_level
log_level
Definition log.h:28
LOG_FATAL
@ LOG_FATAL
Definition log.h:29
log_error
#define log_error(message,...)
Definition log.h:103
fc_assert_ret_val_msg
#define fc_assert_ret_val_msg(condition, val, message,...)
Definition log.h:208
update_map_canvas_visible
void update_map_canvas_visible(void)
Definition mapview_common.c:1860
get_center_tile_mapcanvas
struct tile * get_center_tile_mapcanvas(void)
Definition mapview_common.c:1227
center_tile_mapcanvas
void center_tile_mapcanvas(const struct tile *ptile)
Definition mapview_common.c:1237
center_tile
const struct tile * center_tile
Definition mapview_common.c:80
can_slide
bool can_slide
Definition mapview_common.c:76
fc_calloc
#define fc_calloc(n, esz)
Definition mem.h:38
FC_FREE
#define FC_FREE(ptr)
Definition mem.h:41
fc_strdup
#define fc_strdup(str)
Definition mem.h:43
fc_malloc
#define fc_malloc(sz)
Definition mem.h:34
secfile_load
struct section_file * secfile_load(const char *filename, bool allow_duplicates)
Definition registry.c:50
registry.h
secfile_error
const char * secfile_error(void)
Definition section_file.c:36
section_name
const char * section_name(const struct section *psection)
Definition section_file.c:65
secfile_destroy
void secfile_destroy(struct section_file *secfile)
Definition section_file.c:95
secfile_check_unused
void secfile_check_unused(const struct section_file *secfile)
Definition registry_ini.c:907
secfile_lookup_int
bool secfile_lookup_int(const struct section_file *secfile, int *ival, const char *path,...)
Definition registry_ini.c:1975
secfile_sections_by_name_prefix
struct section_list * secfile_sections_by_name_prefix(const struct section_file *secfile, const char *prefix)
Definition registry_ini.c:2871
secfile_lookup_str_vec
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
Definition registry_ini.c:2239
secfile_entry_lookup
struct entry * secfile_entry_lookup(const struct section_file *secfile, const char *path,...)
Definition registry_ini.c:1857
secfile_lookup_str
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
Definition registry_ini.c:2175
secfile_lookup_int_default
int secfile_lookup_int_default(const struct section_file *secfile, int def, const char *path,...)
Definition registry_ini.c:2000
secfile_entry_by_path
struct entry * secfile_entry_by_path(const struct section_file *secfile, const char *path)
Definition registry_ini.c:1784
secfile_lookup_str_default
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
Definition registry_ini.c:2205
section_list_iterate
#define section_list_iterate(seclist, psection)
Definition registry_ini.h:45
section_list_iterate_end
#define section_list_iterate_end
Definition registry_ini.h:47
level
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:190
fileinfoname
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1101
fileinfolist
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
Definition shared.c:1027
setup_langname
const char * setup_langname(void)
Definition shared.c:1288
get_data_dirs
const struct strvec * get_data_dirs(void)
Definition shared.c:893
spechash.h
speclist.h
crop_sprite
crop_sprite
Definition sprite_g.h:30
get_sprite_dimensions
struct sprite int int int int struct sprite int int float bool smooth get_sprite_dimensions
Definition sprite_g.h:36
strvec_destroy
void strvec_destroy(struct strvec *psv)
Definition string_vector.c:72
strvec_size
size_t strvec_size(const struct strvec *psv)
Definition string_vector.c:344
string_vector.h
strvec_iterate
#define strvec_iterate(psv, str)
Definition string_vector.h:61
strvec_iterate_end
#define strvec_iterate_end
Definition string_vector.h:68
named_sprites
Definition tilespec.c:237
section_file
Definition section_file.h:36
small_sprite
Definition themespec.c:96
small_sprite::height
int height
Definition themespec.c:104
small_sprite::sprite
struct sprite * sprite
Definition themespec.c:109
small_sprite::x
int x
Definition themespec.c:104
small_sprite::file
char * file
Definition themespec.c:100
small_sprite::ref_count
int ref_count
Definition themespec.c:97
small_sprite::width
int width
Definition themespec.c:104
small_sprite::sf
struct specfile * sf
Definition themespec.c:103
small_sprite::hot_y
int hot_y
Definition themespec.c:107
small_sprite::y
int y
Definition themespec.c:104
small_sprite::hot_x
int hot_x
Definition themespec.c:107
specfile
Definition themespec.c:78
specfile::file_name
char * file_name
Definition themespec.c:80
specfile::big_sprite
struct sprite * big_sprite
Definition themespec.c:79
sprite
Definition sprite.h:22
strvec
Definition string_vector.c:29
theme_background_system
Definition themebackgrounds.c:28
theme_color_system
Definition themecolors.c:35
theme
Definition themespec.c:133
theme::sprite_hash
struct small_sprite_hash * sprite_hash
Definition themespec.c:146
theme::small_sprites
struct small_sprite_list * small_sprites
Definition themespec.c:141
theme::default_font_size
int default_font_size
Definition themespec.c:138
theme::color_system
struct theme_color_system * color_system
Definition themespec.c:151
theme::name
char name[512]
Definition themespec.c:134
theme::specfiles
struct specfile_list * specfiles
Definition themespec.c:140
theme::font_filename
char * font_filename
Definition themespec.c:137
theme::priority
int priority
Definition themespec.c:135
theme::background_system
struct theme_background_system * background_system
Definition themespec.c:150
tile
Definition tile.h:50
fc_snprintf
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
sz_strlcpy
#define sz_strlcpy(dest, src)
Definition support.h:189
TRUE
#define TRUE
Definition support.h:46
FALSE
#define FALSE
Definition support.h:47
sprite_vector_iterate_end
#define sprite_vector_iterate_end
Definition tilespec.h:46
sprite_vector_iterate
#define sprite_vector_iterate(sprite_vec, psprite)
Definition tilespec.h:44