[Freeciv-Dev] Re: (PR#14639) Patch for SDL client
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14639 >
Jason Short wrote:
>Aside from this we have a design issue. I do not generally agree with
>GUIs adding "theme" graphics to the tileset: doing so means tilesets
>will not have consistent behavior across GUIs and it makes tileset
>writing a lot harder. Instead I think the "theme" mechanism should be
>used and these graphics should go into a gui-sdl theme.
>
>
Regarding this issue, here is a proof-of-concept patch. It adds
"themespec.c" and "themespec.h" to gui-sdl, which are basically
stripped-down copies of tilespec.c and tilespec.h. This will allow to
describe and handle themes nearly the same way as tilesets. The theme
gets loaded by the client with a call to themespec_try_read(theme_name)
and then all theme sprites can be accessed by
themeset_lookup_sprite_tag_alt(...). A sample .themespec file is attached.
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/graphics.c
devel/client/gui-sdl/graphics.c
--- devel_distclean/client/gui-sdl/graphics.c 2005-12-02 02:46:23.000000000
+0100
+++ devel/client/gui-sdl/graphics.c 2005-12-02 02:50:17.000000000 +0100
@@ -52,7 +52,7 @@
#include "mapview_g.h"
#include "options.h"
-#include "tilespec.h"
+#include "themespec.h"
#include "gui_mem.h"
#include "graphics.h"
#include "gui_zoom.h"
@@ -3415,13 +3415,15 @@
SDL_Surface * get_logo_gfx(void)
{
SDL_Surface *pLogo;
- SDL_Surface *pLogo_Surf = IMG_Load(tileset_mini_intro_filename(tileset));
+
+ SDL_Surface *pLogo_Surf = adj_surf(GET_SURF(themeset_lookup_sprite_tag_alt(
+ themeset, "theme.logo", "", TRUE, "", "")));
assert(pLogo_Surf != NULL);
pLogo = SDL_CreateRGBSurface(SDL_SWSURFACE,
pLogo_Surf->w, pLogo_Surf->h,
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0);
SDL_BlitSurface(pLogo_Surf, NULL, pLogo, NULL);
- FREESURFACE(pLogo_Surf);
+
return pLogo;
}
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_main.c
devel/client/gui-sdl/gui_main.c
--- devel_distclean/client/gui-sdl/gui_main.c 2005-12-02 02:46:23.000000000
+0100
+++ devel/client/gui-sdl/gui_main.c 2005-12-02 02:50:17.000000000 +0100
@@ -84,6 +84,7 @@
#include "spaceshipdlg.h"
#include "resources.h"
#include "tilespec.h"
+#include "themespec.h"
#include "gui_tilespec.h"
#include "messagewin.h"
#include "citydlg.h"
@@ -938,6 +939,8 @@
CF_DRAW_PLAYERS_PEACE_STATUS|
CF_DRAW_PLAYERS_ALLIANCE_STATUS);
+ themespec_try_read("deluxe");
+
tilespec_setup_theme();
tilespec_setup_anim();
tilespec_setup_city_gfx();
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_tilespec.c
devel/client/gui-sdl/gui_tilespec.c
--- devel_distclean/client/gui-sdl/gui_tilespec.c 2005-12-02
02:46:23.000000000 +0100
+++ devel/client/gui-sdl/gui_tilespec.c 2005-12-02 02:50:17.000000000 +0100
@@ -34,6 +34,7 @@
#include "log.h"
#include "tilespec.h"
+#include "themespec.h"
#include "gui_mem.h"
#include "gui_main.h"
@@ -43,15 +44,10 @@
#include "gui_tilespec.h"
-struct sprite* lookup_sprite_tag_alt(struct tileset *t,
- const char *tag, const char *alt,
- bool required, const char *what,
- const char *name);
-
#ifdef SMALL_SCREEN
#define load_GUI_surface(pSpr, pStruct, pSurf, tag) \
do { \
- pSpr = lookup_sprite_tag_alt(tileset, tag, "", TRUE, "", ""); \
+ pSpr = themeset_lookup_sprite_tag_alt(themeset, tag, "", TRUE, "", ""); \
pStruct->pSurf = (pSpr ? GET_SURF(pSpr) : NULL); \
assert(pStruct->pSurf != NULL); \
pStruct->pSurf = ZoomSurface(pStruct->pSurf, 0.5, 0.5, 0); \
@@ -60,7 +56,7 @@
#else
#define load_GUI_surface(pSpr, pStruct, pSurf, tag) \
do { \
- pSpr = lookup_sprite_tag_alt(tileset, tag, "", TRUE, "", ""); \
+ pSpr = themeset_lookup_sprite_tag_alt(themeset, tag, "", TRUE, "", ""); \
pStruct->pSurf = (pSpr ? GET_SURF(pSpr) : NULL); \
assert(pStruct->pSurf != NULL); \
pSpr->psurface = NULL; \
@@ -211,7 +207,8 @@
...
**************************************************************************/
void tilespec_setup_city_gfx(void) {
- struct sprite *pSpr = lookup_sprite_tag_alt(tileset, "theme.city", "", TRUE,
"", "");
+ struct sprite *pSpr = themeset_lookup_sprite_tag_alt(
+ themeset, "theme.city", "", TRUE, "", "");
pCity_Surf = (pSpr ? GET_SURF(pSpr) : NULL);
#ifdef SMALL_SCREEN
@@ -343,7 +340,7 @@
pTheme = MALLOC(sizeof(struct Theme));
- if(!lookup_sprite_tag_alt(tileset, "theme.tech_tree", "", FALSE, "", "")) {
+ if(!themeset_lookup_sprite_tag_alt(themeset, "theme.tech_tree", "", FALSE,
"", "")) {
freelog(LOG_FATAL, "Your current tileset don't contains ""all"" GUI theme
graphic\n"
"Please use other tileset with ""full"" GUI graphic pack (use -t tileset
options)\n"
"If you don't have any tileset with SDLClient GUI theme then go to
freeciv\n"
@@ -491,7 +488,7 @@
do { \
iter = 0; \
my_snprintf(cBuf , sizeof(cBuf), "%s_%d", Tag, iter); \
- while(lookup_sprite_tag_alt(tileset, cBuf, "", FALSE, "", "")) { \
+ while(themeset_lookup_sprite_tag_alt(themeset, cBuf, "", FALSE, "", "")) {
\
iter++; \
my_snprintf(cBuf , sizeof(cBuf), "%s_%d", Tag, iter); \
} \
@@ -500,7 +497,7 @@
pAnim->Cursors.Type = CALLOC(num + 1, sizeof(SDL_Cursor *)); \
for( iter=0; iter<num; iter++) { \
my_snprintf(cBuf,sizeof(cBuf), "%s_%d", Tag, iter); \
- pSpr = lookup_sprite_tag_alt(tileset, cBuf, "", FALSE, "", ""); \
+ pSpr = themeset_lookup_sprite_tag_alt(themeset, cBuf, "", FALSE, "",
""); \
image = (pSpr ? GET_SURF(pSpr) : NULL); \
assert(image != NULL); \
if (center) { \
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/Makefile.am
devel/client/gui-sdl/Makefile.am
--- devel_distclean/client/gui-sdl/Makefile.am 2005-11-29 17:51:46.000000000
+0100
+++ devel/client/gui-sdl/Makefile.am 2005-12-02 02:50:17.000000000 +0100
@@ -82,6 +82,7 @@
spaceshipdlg.c \
spaceshipdlg.h \
themes.c \
+ themespec.c \
wldlg.c \
wldlg.h \
gui_string.c \
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/themespec.c
devel/client/gui-sdl/themespec.c
--- devel_distclean/client/gui-sdl/themespec.c 1970-01-01 01:00:00.000000000
+0100
+++ devel/client/gui-sdl/themespec.c 2005-12-02 02:50:17.000000000 +0100
@@ -0,0 +1,1087 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+***********************************************************************/
+
+/**********************************************************************
+ Functions for handling the themespec files which describe
+ the files and contents of themesets.
+ original author: David Pfitzner <dwp@xxxxxxxxxxxxxx>
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h> /* exit */
+#include <string.h>
+
+#include "astring.h"
+#include "capability.h"
+#include "fcintl.h"
+#include "government.h"
+#include "hash.h"
+#include "log.h"
+#include "map.h"
+#include "mem.h"
+#include "nation.h"
+#include "player.h"
+#include "registry.h"
+#include "shared.h"
+#include "support.h"
+#include "unit.h"
+
+#include "dialogs_g.h"
+#include "graphics_g.h"
+#include "gui_main_g.h"
+#include "mapview_g.h" /* for update_map_canvas_visible */
+#include "themes_g.h"
+
+#include "civclient.h" /* for get_client_state() */
+#include "themes_common.h"
+
+#include "themespec.h"
+
+#define THEMESPEC_SUFFIX ".themespec"
+
+#if 0
+/* TODO: may be useful for theme sprites, too */
+struct named_sprites {
+ ....
+};
+#endif
+
+struct specfile {
+ struct sprite *big_sprite;
+ char *file_name;
+};
+
+#define SPECLIST_TAG specfile
+#define SPECLIST_TYPE struct specfile
+#include "speclist.h"
+
+#define specfile_list_iterate(list, pitem) \
+ TYPED_LIST_ITERATE(struct specfile, list, pitem)
+#define specfile_list_iterate_end LIST_ITERATE_END
+
+/*
+ * Information about an individual sprite. All fields except 'sprite' are
+ * filled at the time of the scan of the specfile. 'Sprite' is
+ * set/cleared on demand in load_sprite/unload_sprite.
+ */
+struct small_sprite {
+ int ref_count;
+
+ /* The sprite is in this file. */
+ char *file;
+
+ /* Or, the sprite is in this file at the location. */
+ struct specfile *sf;
+ int x, y, width, height;
+
+ /* A little more (optional) data. */
+ int hot_x, hot_y;
+
+ struct sprite *sprite;
+};
+
+#define SPECLIST_TAG small_sprite
+#define SPECLIST_TYPE struct small_sprite
+#include "speclist.h"
+
+#define small_sprite_list_iterate(list, pitem) \
+ TYPED_LIST_ITERATE(struct small_sprite, list, pitem)
+#define small_sprite_list_iterate_end LIST_ITERATE_END
+
+struct themeset {
+ char name[512];
+ int priority;
+
+ char *main_intro_filename;
+ char *minimap_intro_filename;
+
+ struct specfile_list *specfiles;
+ struct small_sprite_list *small_sprites;
+
+ /*
+ * This hash table maps themespec tags to struct small_sprites.
+ */
+ struct hash_table *sprite_hash;
+
+/* struct named_sprites sprites;*/
+
+ struct color_system *color_system;
+};
+
+struct themeset *themeset;
+
+#define TILESPEC_CAPSTR "+themespec3 duplicates_ok +Freeciv.Devel.2005.Nov.25"
+/*
+ * Tilespec capabilities acceptable to this program:
+ *
+ * +themespec3 - basic format; required
+ *
+ * duplicates_ok - we can handle existence of duplicate tags
+ * (lattermost tag which appears is used; themesets which
+ * have duplicates should specify "+duplicates_ok")
+ */
+
+#define SPEC_CAPSTR "+spec3"
+/*
+ * Individual spec file capabilities acceptable to this program:
+ * +spec3 - basic format, required
+ */
+
+
+/****************************************************************************
+ Return the name of the given themeset.
+****************************************************************************/
+const char *themeset_get_name(const struct themeset *t)
+{
+ return t->name;
+}
+
+/****************************************************************************
+ Return the path within the data directories where the main intro graphics
+ file can be found. (It is left up to the GUI code to load and unload this
+ file.)
+****************************************************************************/
+const char *themeset_main_intro_filename(const struct themeset *t)
+{
+ return t->main_intro_filename;
+}
+
+/****************************************************************************
+ Return the path within the data directories where the mini intro graphics
+ file can be found. (It is left up to the GUI code to load and unload this
+ file.)
+****************************************************************************/
+const char *themeset_mini_intro_filename(const struct themeset *t)
+{
+ return t->minimap_intro_filename;
+}
+
+/**************************************************************************
+ Initialize.
+**************************************************************************/
+static struct themeset *themeset_new(void)
+{
+ struct themeset *t = fc_calloc(1, sizeof(*t));
+
+ t->specfiles = specfile_list_new();
+ t->small_sprites = small_sprite_list_new();
+ return t;
+}
+
+/**********************************************************************
+ Returns a static list of themesets available on the system by
+ searching all data directories for files matching THEMESPEC_SUFFIX.
+ The list is NULL-terminated.
+***********************************************************************/
+const char **get_themeset_list(void)
+{
+ static const char **themesets = NULL;
+
+ if (!themesets) {
+ /* Note: this means you must restart the client after installing a new
+ themeset. */
+ char **list = datafilelist(THEMESPEC_SUFFIX);
+ int i, count = 0;
+
+ for (i = 0; list[i]; i++) {
+ struct themeset *t = themeset_read_toplevel(list[i]);
+
+ if (t) {
+ themesets = fc_realloc(themesets, (count + 1) * sizeof(*themesets));
+ themesets[count] = list[i];
+ count++;
+ themeset_free(t);
+ } else {
+ free(list[i]);
+ }
+ }
+
+ themesets = fc_realloc(themesets, (count + 1) * sizeof(*themesets));
+ themesets[count] = NULL;
+ }
+
+ return themesets;
+}
+
+/**********************************************************************
+ Gets full filename for themespec file, based on input name.
+ Returned data is allocated, and freed by user as required.
+ Input name may be null, in which case uses default.
+ Falls back to default if can't find specified name;
+ dies if can't find default.
+***********************************************************************/
+static char *themespec_fullname(const char *themeset_name)
+{
+ if (themeset_name) {
+ char fname[strlen(themeset_name) + strlen(THEMESPEC_SUFFIX) + 1], *dname;
+
+ my_snprintf(fname, sizeof(fname),
+ "%s%s", themeset_name, THEMESPEC_SUFFIX);
+
+ dname = datafilename(fname);
+
+ if (dname) {
+ return mystrdup(dname);
+ }
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ Checks options in filename match what we require and support.
+ Die if not.
+ 'which' should be "themespec" or "spec".
+***********************************************************************/
+static bool check_themespec_capabilities(struct section_file *file,
+ const char *which,
+ const char *us_capstr,
+ const char *filename)
+{
+ char *file_capstr = secfile_lookup_str(file, "%s.options", which);
+
+ if (!has_capabilities(us_capstr, file_capstr)) {
+ freelog(LOG_DEBUG, _("%s file appears incompatible:\n"
+ "file: \"%s\"\n"
+ "file options: %s\n"
+ "supported options: %s"),
+ which, filename, file_capstr, us_capstr);
+ return FALSE;
+ }
+ if (!has_capabilities(file_capstr, us_capstr)) {
+ freelog(LOG_DEBUG, _("%s file claims required option(s)"
+ " which we don't support:\n"
+ "file: \"%s\"\n"
+ "file options: %s\n"
+ "supported options: %s"),
+ which, filename, file_capstr, us_capstr);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************
+ Frees the themespec toplevel data, in preparation for re-reading it.
+
+ See themespec_read_toplevel().
+***********************************************************************/
+static void themeset_free_toplevel(struct themeset *t)
+{
+ if (t->main_intro_filename) {
+ free(t->main_intro_filename);
+ t->main_intro_filename = NULL;
+ }
+ if (t->minimap_intro_filename) {
+ free(t->minimap_intro_filename);
+ t->minimap_intro_filename = NULL;
+ }
+
+ if (t->color_system) {
+ color_system_free(t->color_system);
+ t->color_system = NULL;
+ }
+}
+
+/**************************************************************************
+ Clean up.
+**************************************************************************/
+void themeset_free(struct themeset *t)
+{
+ themeset_free_tiles(t);
+ themeset_free_toplevel(t);
+ specfile_list_free(t->specfiles);
+ small_sprite_list_free(t->small_sprites);
+ free(t);
+}
+
+/**********************************************************************
+ Read a new themespec in when first starting the game.
+
+ Call this function with the (guessed) name of the themeset, when
+ starting the client.
+***********************************************************************/
+void themespec_try_read(const char *themeset_name)
+{
+ if (!(themeset = themeset_read_toplevel(themeset_name))) {
+ char **list = datafilelist(THEMESPEC_SUFFIX);
+ int i;
+
+ for (i = 0; list[i]; i++) {
+ struct themeset *t = themeset_read_toplevel(list[i]);
+
+ if (t) {
+ if (!themeset || t->priority > themeset->priority) {
+ themeset = t;
+ } else {
+ themeset_free(t);
+ }
+ }
+ free(list[i]);
+ }
+ free(list);
+
+ if (!themeset) {
+ freelog(LOG_FATAL, _("No usable default themeset found, aborting!"));
+ exit(EXIT_FAILURE);
+ }
+
+ freelog(LOG_NORMAL, _("Trying \"%s\" themeset."), themeset->name);
+ }
+/* sz_strlcpy(default_themeset_name, themeset_get_name(themeset));*/
+}
+
+/**********************************************************************
+ Read a new themespec in from scratch.
+
+ Unlike the initial reading code, which reads pieces one at a time,
+ this gets rid of the old data and reads in the new all at once. If the
+ new themeset fails to load the old themeset may be reloaded; otherwise the
+ client will exit. If a NULL name is given the current themeset will be
+ reread.
+
+ It will also call the necessary functions to redraw the graphics.
+***********************************************************************/
+void themespec_reread(const char *new_themeset_name)
+{
+ struct tile *center_tile;
+ enum client_states state = get_client_state();
+ const char *name = new_themeset_name ? new_themeset_name : themeset->name;
+ char themeset_name[strlen(name) + 1], old_name[strlen(themeset->name) + 1];
+
+ /* Make local copies since these values may be freed down below */
+ sz_strlcpy(themeset_name, name);
+ sz_strlcpy(old_name, themeset->name);
+
+ freelog(LOG_NORMAL, "Loading themeset %s.", themeset_name);
+
+ /* Step 0: Record old data.
+ *
+ * We record the current mapcanvas center, etc.
+ */
+ center_tile = get_center_tile_mapcanvas();
+
+ /* Step 1: Cleanup.
+ *
+ * We free all old data in preparation for re-reading it.
+ */
+ themeset_free_tiles(themeset);
+ themeset_free_toplevel(themeset);
+
+ /* Step 2: Read.
+ *
+ * We read in the new themeset. This should be pretty straightforward.
+ */
+ if (!(themeset = themeset_read_toplevel(themeset_name))) {
+ if (!(themeset = themeset_read_toplevel(old_name))) {
+ die("Failed to re-read the currently loaded themeset.");
+ }
+ }
+/* sz_strlcpy(default_themeset_name, themeset->name);*/
+ themeset_load_tiles(themeset);
+
+ /* Step 3: Setup
+ *
+ * This is a seriously sticky problem. On startup, we build a hash
+ * from all the sprite data. Then, when we connect to a server, the
+ * server sends us ruleset data a piece at a time and we use this data
+ * to assemble the sprite structures. But if we change while connected
+ * we have to reassemble all of these. This should just involve
+ * calling themespec_setup_*** on everything. But how do we tell what
+ * "everything" is?
+ *
+ * The below code just does things straightforwardly, by setting up
+ * each possible sprite again. Hopefully it catches everything, and
+ * doesn't mess up too badly if we change themesets while not connected
+ * to a server.
+ */
+ if (state < CLIENT_GAME_RUNNING_STATE) {
+ /* The ruleset data is not sent until this point. */
+ return;
+ }
+
+ /* Step 4: Draw.
+ *
+ * Do any necessary redraws.
+ */
+ if (state < CLIENT_GAME_RUNNING_STATE) {
+ /* Unless the client state is playing a game or in gameover,
+ we don't want/need to redraw. */
+ return;
+ }
+ popdown_all_game_dialogs();
+ generate_citydlg_dimensions();
+/* themeset_changed();*/
+ can_slide = FALSE;
+ center_tile_mapcanvas(center_tile);
+ /* update_map_cavnas_visible forces a full redraw. Otherwise with fast
+ * drawing we might not get one. Of course this is slower. */
+ update_map_canvas_visible();
+ can_slide = TRUE;
+}
+
+/**************************************************************************
+ This is merely a wrapper for themespec_reread (above) for use in
+ options.c and the client local options dialog.
+**************************************************************************/
+void themespec_reread_callback(struct client_option *option)
+{
+ assert(option->p_string_value && *option->p_string_value != '\0');
+ themespec_reread(option->p_string_value);
+}
+
+/**************************************************************************
+ Loads the given graphics file (found in the data path) into a newly
+ allocated sprite.
+**************************************************************************/
+static struct sprite *load_gfx_file(const char *gfx_filename)
+{
+ const char **gfx_fileexts = gfx_fileextensions(), *gfx_fileext;
+ struct sprite *s;
+
+ /* Try out all supported file extensions to find one that works. */
+ while ((gfx_fileext = *gfx_fileexts++)) {
+ char *real_full_name;
+ char full_name[strlen(gfx_filename) + strlen(gfx_fileext) + 2];
+
+ sprintf(full_name, "%s.%s", gfx_filename, gfx_fileext);
+ if ((real_full_name = datafilename(full_name))) {
+ freelog(LOG_DEBUG, "trying to load gfx file %s", real_full_name);
+ s = load_gfxfile(real_full_name);
+ if (s) {
+ return s;
+ }
+ }
+ }
+
+ freelog(LOG_VERBOSE, "Could not load gfx file %s.", gfx_filename);
+ return NULL;
+}
+
+/**************************************************************************
+ Ensure that the big sprite of the given spec file is loaded.
+**************************************************************************/
+static void ensure_big_sprite(struct specfile *sf)
+{
+ struct section_file the_file, *file = &the_file;
+ const char *gfx_filename;
+
+ if (sf->big_sprite) {
+ /* Looks like it's already loaded. */
+ return;
+ }
+
+ /* Otherwise load it. The big sprite will sometimes be freed and will have
+ * to be reloaded, but most of the time it's just loaded once, the small
+ * sprites are extracted, and then it's freed. */
+ if (!section_file_load(file, sf->file_name)) {
+ freelog(LOG_FATAL, _("Could not open \"%s\"."), sf->file_name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!check_themespec_capabilities(file, "spec",
+ SPEC_CAPSTR, sf->file_name)) {
+ exit(EXIT_FAILURE);
+ }
+
+ gfx_filename = secfile_lookup_str(file, "file.gfx");
+
+ sf->big_sprite = load_gfx_file(gfx_filename);
+
+ if (!sf->big_sprite) {
+ freelog(LOG_FATAL, _("Couldn't load gfx file for the spec file %s"),
+ sf->file_name);
+ exit(EXIT_FAILURE);
+ }
+ section_file_free(file);
+}
+
+/**************************************************************************
+ Scan all sprites declared in the given specfile. This means that the
+ positions of the sprites in the big_sprite are saved in the
+ small_sprite structs.
+**************************************************************************/
+static void scan_specfile(struct themeset *t, struct specfile *sf,
+ bool duplicates_ok)
+{
+ struct section_file the_file, *file = &the_file;
+ char **gridnames;
+ int num_grids, i;
+
+ if (!section_file_load(file, sf->file_name)) {
+ freelog(LOG_FATAL, _("Could not open \"%s\"."), sf->file_name);
+ exit(EXIT_FAILURE);
+ }
+ if (!check_themespec_capabilities(file, "spec",
+ SPEC_CAPSTR, sf->file_name)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* currently unused */
+ (void) section_file_lookup(file, "info.artists");
+
+ gridnames = secfile_get_secnames_prefix(file, "grid_", &num_grids);
+
+ for (i = 0; i < num_grids; i++) {
+ int j, k;
+ int x_top_left, y_top_left, dx, dy;
+ int pixel_border;
+
+ pixel_border = secfile_lookup_int_default(file, 0, "%s.pixel_border",
+ gridnames[i]);
+
+ x_top_left = secfile_lookup_int(file, "%s.x_top_left", gridnames[i]);
+ y_top_left = secfile_lookup_int(file, "%s.y_top_left", gridnames[i]);
+ dx = secfile_lookup_int(file, "%s.dx", gridnames[i]);
+ dy = secfile_lookup_int(file, "%s.dy", gridnames[i]);
+
+ j = -1;
+ while (section_file_lookup(file, "%s.tiles%d.tag", gridnames[i], ++j)) {
+ struct small_sprite *ss = fc_malloc(sizeof(*ss));
+ int row, column;
+ int x1, y1;
+ char **tags;
+ int num_tags;
+ int hot_x, hot_y;
+
+ row = secfile_lookup_int(file, "%s.tiles%d.row", gridnames[i], j);
+ column = secfile_lookup_int(file, "%s.tiles%d.column", gridnames[i], j);
+ tags = secfile_lookup_str_vec(file, &num_tags, "%s.tiles%d.tag",
+ gridnames[i], j);
+ hot_x = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_x",
+ gridnames[i], j);
+ hot_y = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_y",
+ gridnames[i], j);
+
+ /* there must be at least 1 because of the while(): */
+ assert(num_tags > 0);
+
+ x1 = x_top_left + (dx + pixel_border) * column;
+ y1 = y_top_left + (dy + pixel_border) * row;
+
+ ss->ref_count = 0;
+ ss->file = NULL;
+ ss->x = x1;
+ ss->y = y1;
+ ss->width = dx;
+ ss->height = dy;
+ ss->sf = sf;
+ ss->sprite = NULL;
+ ss->hot_x = hot_x;
+ ss->hot_y = hot_y;
+
+ small_sprite_list_prepend(t->small_sprites, ss);
+
+ if (!duplicates_ok) {
+ for (k = 0; k < num_tags; k++) {
+ if (!hash_insert(t->sprite_hash, mystrdup(tags[k]), ss)) {
+ freelog(LOG_ERROR, "warning: already have a sprite for %s",
tags[k]);
+ }
+ }
+ } else {
+ for (k = 0; k < num_tags; k++) {
+ (void) hash_replace(t->sprite_hash, mystrdup(tags[k]), ss);
+ }
+ }
+
+ free(tags);
+ tags = NULL;
+ }
+ }
+ free(gridnames);
+ gridnames = NULL;
+
+ /* Load "extra" sprites. Each sprite is one file. */
+ i = -1;
+ while (secfile_lookup_str_default(file, NULL, "extra.sprites%d.tag", ++i)) {
+ struct small_sprite *ss = fc_malloc(sizeof(*ss));
+ char **tags;
+ char *filename;
+ int num_tags, k;
+ int hot_x, hot_y;
+
+ tags
+ = secfile_lookup_str_vec(file, &num_tags, "extra.sprites%d.tag", i);
+ filename = secfile_lookup_str(file, "extra.sprites%d.file", i);
+
+ hot_x = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_x", i);
+ hot_y = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_y", i);
+
+ ss->ref_count = 0;
+ ss->file = mystrdup(filename);
+ ss->sf = NULL;
+ ss->sprite = NULL;
+ ss->hot_x = hot_x;
+ ss->hot_y = hot_y;
+
+ small_sprite_list_prepend(t->small_sprites, ss);
+
+ if (!duplicates_ok) {
+ for (k = 0; k < num_tags; k++) {
+ if (!hash_insert(t->sprite_hash, mystrdup(tags[k]), ss)) {
+ freelog(LOG_ERROR, "warning: already have a sprite for %s", tags[k]);
+ }
+ }
+ } else {
+ for (k = 0; k < num_tags; k++) {
+ (void) hash_replace(t->sprite_hash, mystrdup(tags[k]), ss);
+ }
+ }
+ free(tags);
+ }
+
+ section_file_check_unused(file, sf->file_name);
+ section_file_free(file);
+}
+
+/**********************************************************************
+ Returns the correct name of the gfx file (with path and extension)
+ Must be free'd when no longer used
+***********************************************************************/
+static char *themespec_gfx_filename(const char *gfx_filename)
+{
+ const char *gfx_current_fileext;
+ const char **gfx_fileexts = gfx_fileextensions();
+
+ while((gfx_current_fileext = *gfx_fileexts++))
+ {
+ char *full_name =
+ fc_malloc(strlen(gfx_filename) + strlen(gfx_current_fileext) + 2);
+ char *real_full_name;
+
+ sprintf(full_name,"%s.%s",gfx_filename,gfx_current_fileext);
+
+ real_full_name = datafilename(full_name);
+ free(full_name);
+ if (real_full_name) {
+ return mystrdup(real_full_name);
+ }
+ }
+
+ freelog(LOG_FATAL, _("Couldn't find a supported gfx file extension for %s"),
+ gfx_filename);
+ exit(EXIT_FAILURE);
+ return NULL;
+}
+
+/**********************************************************************
+ Finds and reads the toplevel themespec file based on given name.
+ Sets global variables, including tile sizes and full names for
+ intro files.
+***********************************************************************/
+struct themeset *themeset_read_toplevel(const char *themeset_name)
+{
+ struct section_file the_file, *file = &the_file;
+ char *fname, *c;
+ int i;
+ int num_spec_files;
+ char **spec_filenames;
+ char *file_capstr;
+ bool duplicates_ok;
+ struct themeset *t = themeset_new();
+
+ fname = themespec_fullname(themeset_name);
+ if (!fname) {
+ themeset_free(t);
+ return NULL;
+ }
+ freelog(LOG_VERBOSE, "themespec file is %s", fname);
+
+ if (!section_file_load(file, fname)) {
+ freelog(LOG_ERROR, _("Could not open \"%s\"."), fname);
+ section_file_free(file);
+ free(fname);
+ themeset_free(t);
+ return NULL;
+ }
+
+ if (!check_themespec_capabilities(file, "themespec",
+ TILESPEC_CAPSTR, fname)) {
+ section_file_free(file);
+ free(fname);
+ themeset_free(t);
+ return NULL;
+ }
+
+ file_capstr = secfile_lookup_str(file, "%s.options", "themespec");
+ duplicates_ok = has_capabilities("+duplicates_ok", file_capstr);
+
+ (void) section_file_lookup(file, "themespec.name"); /* currently unused */
+
+ sz_strlcpy(t->name, themeset_name);
+ t->priority = secfile_lookup_int(file, "themespec.priority");
+
+ c = secfile_lookup_str(file, "themespec.main_intro_file");
+ t->main_intro_filename = themespec_gfx_filename(c);
+ freelog(LOG_DEBUG, "intro file %s", t->main_intro_filename);
+
+ c = secfile_lookup_str(file, "themespec.minimap_intro_file");
+ t->minimap_intro_filename = themespec_gfx_filename(c);
+ freelog(LOG_DEBUG, "radar file %s", t->minimap_intro_filename);
+
+ spec_filenames = secfile_lookup_str_vec(file, &num_spec_files,
+ "themespec.files");
+ if (num_spec_files == 0) {
+ freelog(LOG_ERROR, "No tile files specified in \"%s\"", fname);
+ section_file_free(file);
+ free(fname);
+ themeset_free(t);
+ return NULL;
+ }
+
+ assert(t->sprite_hash == NULL);
+ t->sprite_hash = hash_new(hash_fval_string, hash_fcmp_string);
+ for (i = 0; i < num_spec_files; i++) {
+ struct specfile *sf = fc_malloc(sizeof(*sf));
+ char *dname;
+
+ freelog(LOG_DEBUG, "spec file %s", spec_filenames[i]);
+
+ sf->big_sprite = NULL;
+ dname = datafilename(spec_filenames[i]);
+ if (!dname) {
+ section_file_free(file);
+ free(fname);
+ themeset_free(t);
+ return NULL;
+ }
+ sf->file_name = mystrdup(dname);
+ scan_specfile(t, sf, duplicates_ok);
+
+ specfile_list_prepend(t->specfiles, sf);
+ }
+ free(spec_filenames);
+
+ t->color_system = color_system_read(file);
+
+ section_file_check_unused(file, fname);
+
+ section_file_free(file);
+ freelog(LOG_VERBOSE, "finished reading %s", fname);
+ free(fname);
+
+ return t;
+}
+
+
+/**************************************************************************
+ Loads the sprite. If the sprite is already loaded a reference
+ counter is increased. Can return NULL if the sprite couldn't be
+ loaded.
+**************************************************************************/
+static struct sprite *load_sprite(struct themeset *t, const char *tag_name)
+{
+ /* Lookup information about where the sprite is found. */
+ struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name);
+
+ freelog(LOG_DEBUG, "load_sprite(tag='%s')", tag_name);
+ if (!ss) {
+ return NULL;
+ }
+
+ assert(ss->ref_count >= 0);
+
+ if (!ss->sprite) {
+ /* If the sprite hasn't been loaded already, then load it. */
+ assert(ss->ref_count == 0);
+ if (ss->file) {
+ ss->sprite = load_gfx_file(ss->file);
+ if (!ss->sprite) {
+ freelog(LOG_FATAL, _("Couldn't load gfx file %s for sprite %s"),
+ ss->file, tag_name);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ int sf_w, sf_h;
+
+ ensure_big_sprite(ss->sf);
+ get_sprite_dimensions(ss->sf->big_sprite, &sf_w, &sf_h);
+ if (ss->x < 0 || ss->x + ss->width > sf_w
+ || ss->y < 0 || ss->y + ss->height > sf_h) {
+ freelog(LOG_ERROR,
+ "Sprite '%s' in file '%s' isn't within the image!",
+ tag_name, ss->sf->file_name);
+ return NULL;
+ }
+ ss->sprite =
+ crop_sprite(ss->sf->big_sprite, ss->x, ss->y, ss->width, ss->height,
+ NULL, -1, -1);
+ }
+ }
+
+ /* Track the reference count so we know when to free the sprite. */
+ ss->ref_count++;
+
+ return ss->sprite;
+}
+
+/**************************************************************************
+ Unloads the sprite. Decrease the reference counter. If the last
+ reference is removed the sprite is freed.
+**************************************************************************/
+static void unload_sprite(struct themeset *t, const char *tag_name)
+{
+ struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name);
+
+ assert(ss);
+ assert(ss->ref_count >= 1);
+ assert(ss->sprite);
+
+ ss->ref_count--;
+
+ if (ss->ref_count == 0) {
+ /* Nobody's using the sprite anymore, so we should free it. We know
+ * where to find it if we need it again. */
+ freelog(LOG_DEBUG, "freeing sprite '%s'", tag_name);
+ free_sprite(ss->sprite);
+ ss->sprite = NULL;
+ }
+}
+
+/****************************************************************************
+ Insert a generated sprite into the existing sprite hash with a given
+ name.
+
+ This means the sprite can now be accessed via the tag, and will be
+ automatically freed along with the themeset. In other words, you should
+ only do this with sprites you've just allocated, and only on tags that
+ are unused!
+****************************************************************************/
+static void insert_sprite(struct themeset *t, const char *tag_name,
+ struct sprite *sprite)
+{
+ struct small_sprite *ss = fc_calloc(sizeof(*ss), 1);
+
+ assert(load_sprite(t, tag_name) == 0);
+ ss->ref_count = 1;
+ ss->sprite = sprite;
+ small_sprite_list_prepend(t->small_sprites, ss);
+ if (!hash_insert(t->sprite_hash, mystrdup(tag_name), ss)) {
+ freelog(LOG_ERROR, "warning: already have a sprite for %s", tag_name);
+ }
+}
+
+/**************************************************************************
+ Return TRUE iff the specified sprite exists in the themeset (whether
+ or not it is currently loaded).
+**************************************************************************/
+static bool sprite_exists(const struct themeset *t, const char *tag_name)
+{
+ /* Lookup information about where the sprite is found. */
+ struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name);
+
+ return (ss != NULL);
+}
+
+/* Not very safe, but convenient: */
+#define SET_SPRITE(field, tag) \
+ do { \
+ t->sprites.field = load_sprite(t, tag); \
+ if (!t->sprites.field) { \
+ die("Sprite tag %s missing.", tag); \
+ } \
+ } while(FALSE)
+
+/* Sets sprites.field to tag or (if tag isn't available) to alt */
+#define SET_SPRITE_ALT(field, tag, alt)
\
+ do { \
+ t->sprites.field = load_sprite(t, tag); \
+ if (!t->sprites.field) { \
+ t->sprites.field = load_sprite(t, alt); \
+ } \
+ if (!t->sprites.field) { \
+ die("Sprite tag %s and alternate %s are both missing.", tag, alt); \
+ } \
+ } while(FALSE)
+
+/* Sets sprites.field to tag, or NULL if not available */
+#define SET_SPRITE_OPT(field, tag) \
+ t->sprites.field = load_sprite(t, tag)
+
+#define SET_SPRITE_ALT_OPT(field, tag, alt) \
+ do { \
+ t->sprites.field = themeset_lookup_sprite_tag_alt(t, tag, alt, FALSE,
\
+ "sprite", #field); \
+ } while (FALSE)
+
+/**********************************************************************
+ Initialize 'sprites' structure based on hardwired tags which
+ freeciv always requires.
+***********************************************************************/
+static void themeset_lookup_sprite_tags(struct themeset *t)
+{
+ /* TODO: move the lookup code from gui_tilespec.c to this place? */
+}
+
+/**************************************************************************
+ Frees any internal buffers which are created by load_sprite. Should
+ be called after the last (for a given period of time) load_sprite
+ call. This saves a fair amount of memory, but it will take extra time
+ the next time we start loading sprites again.
+**************************************************************************/
+static void finish_loading_sprites(struct themeset *t)
+{
+ specfile_list_iterate(t->specfiles, sf) {
+ if (sf->big_sprite) {
+ free_sprite(sf->big_sprite);
+ sf->big_sprite = NULL;
+ }
+ } specfile_list_iterate_end;
+}
+/**********************************************************************
+ Load the tiles; requires themespec_read_toplevel() called previously.
+ Leads to tile_sprites being allocated and filled with pointers
+ to sprites. Also sets up and populates sprite_hash, and calls func
+ to initialize 'sprites' structure.
+***********************************************************************/
+void themeset_load_tiles(struct themeset *t)
+{
+ themeset_lookup_sprite_tags(t);
+ finish_loading_sprites(t);
+}
+
+/**********************************************************************
+ Lookup sprite to match tag, or else to match alt if don't find,
+ or else return NULL, and emit log message.
+***********************************************************************/
+struct sprite* themeset_lookup_sprite_tag_alt(struct themeset *t,
+ const char *tag, const char *alt,
+ bool required, const char *what,
+ const char *name)
+{
+ struct sprite *sp;
+
+ /* (should get sprite_hash before connection) */
+ if (!t->sprite_hash) {
+ die("attempt to lookup for %s %s before sprite_hash setup", what, name);
+ }
+
+ sp = load_sprite(t, tag);
+ if (sp) return sp;
+
+ sp = load_sprite(t, alt);
+ if (sp) {
+ freelog(LOG_VERBOSE,
+ "Using alternate graphic %s (instead of %s) for %s %s",
+ alt, tag, what, name);
+ return sp;
+ }
+
+ freelog(required ? LOG_FATAL : LOG_VERBOSE,
+ _("Don't have graphics tags %s or %s for %s %s"),
+ tag, alt, what, name);
+ if (required) {
+ exit(EXIT_FAILURE);
+ }
+ return NULL;
+}
+
+#define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2)
+#define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height)
+
+#define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \
+ (assert(s != NULL), \
+ sprs->sprite = s, \
+ sprs->foggable = (draw_fog && t->fogstyle == FOG_AUTO), \
+ sprs->offset_x = x_offset, \
+ sprs->offset_y = y_offset, \
+ sprs++)
+#define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, TRUE, 0, 0)
+#define ADD_SPRITE_FULL(s) \
+ ADD_SPRITE(s, TRUE, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET)
+
+/****************************************************************************
+ This patch unloads all sprites from the sprite hash (the hash itself
+ is left intact).
+****************************************************************************/
+static void unload_all_sprites(struct themeset *t)
+{
+ if (t->sprite_hash) {
+ int i, entries = hash_num_entries(t->sprite_hash);
+
+ for (i = 0; i < entries; i++) {
+ const char *tag_name = hash_key_by_number(t->sprite_hash, i);
+ struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name);
+
+ while (ss->ref_count > 0) {
+ unload_sprite(t, tag_name);
+ }
+ }
+ }
+}
+
+/**********************************************************************
+...
+***********************************************************************/
+void themeset_free_tiles(struct themeset *t)
+{
+ int i;
+
+ freelog(LOG_DEBUG, "themespec_free_tiles");
+
+ unload_all_sprites(t);
+
+ if (t->sprite_hash) {
+ const int entries = hash_num_entries(t->sprite_hash);
+
+ for (i = 0; i < entries; i++) {
+ const char *key = hash_key_by_number(t->sprite_hash, 0);
+
+ hash_delete_entry(t->sprite_hash, key);
+ free((void *) key);
+ }
+
+ hash_free(t->sprite_hash);
+ t->sprite_hash = NULL;
+ }
+
+ small_sprite_list_iterate(t->small_sprites, ss) {
+ small_sprite_list_unlink(t->small_sprites, ss);
+ if (ss->file) {
+ free(ss->file);
+ }
+ assert(ss->sprite == NULL);
+ free(ss);
+ } small_sprite_list_iterate_end;
+
+ specfile_list_iterate(t->specfiles, sf) {
+ specfile_list_unlink(t->specfiles, sf);
+ free(sf->file_name);
+ if (sf->big_sprite) {
+ free_sprite(sf->big_sprite);
+ sf->big_sprite = NULL;
+ }
+ free(sf);
+ } specfile_list_iterate_end;
+
+#if 0
+ /* TODO: may be useful for theme sprites, too */
+ sprite_vector_iterate(&t->sprites.****, psprite) {
+ free_sprite(*psprite);
+ } sprite_vector_iterate_end;
+ sprite_vector_free(&t->sprites.****);
+#endif
+}
+
+/****************************************************************************
+ Return the themeset's color system.
+****************************************************************************/
+struct color_system *themeset_get_color_system(const struct themeset *t)
+{
+ return t->color_system;
+}
diff -urNbB -X devel/diff_ignore devel_distclean/client/gui-sdl/themespec.h
devel/client/gui-sdl/themespec.h
--- devel_distclean/client/gui-sdl/themespec.h 1970-01-01 01:00:00.000000000
+0100
+++ devel/client/gui-sdl/themespec.h 2005-12-02 03:33:17.000000000 +0100
@@ -0,0 +1,56 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+***********************************************************************/
+
+/**********************************************************************
+ Reading and using the themespec files, which describe
+ the files and contents of themesets.
+***********************************************************************/
+#ifndef FC__THEMESPEC_H
+#define FC__THEMESPEC_H
+
+#include "fc_types.h"
+
+#include "options.h"
+
+struct sprite; /* opaque; gui-dep */
+
+struct themeset;
+
+extern struct themeset *themeset;
+
+const char **get_themeset_list(void);
+
+struct themeset *themeset_read_toplevel(const char *themeset_name);
+void themeset_free(struct themeset *themeset);
+void themeset_load_tiles(struct themeset *t);
+void themeset_free_tiles(struct themeset *t);
+
+void themespec_try_read(const char *themeset_name);
+void themespec_reread(const char *themeset_name);
+void themespec_reread_callback(struct client_option *option);
+
+struct sprite* themeset_lookup_sprite_tag_alt(struct themeset *t,
+ const char *tag, const char *alt,
+ bool required, const char *what,
+ const char *name);
+
+struct color_system;
+struct color_system *themeset_get_color_system(const struct themeset *t);
+
+/* Tileset accessor functions. */
+const char *themeset_get_name(const struct themeset *t);
+const char *themeset_main_intro_filename(const struct themeset *t);
+const char *themeset_mini_intro_filename(const struct themeset *t);
+void themeset_use_prefered_theme(const struct themeset *t);
+
+#endif /* FC__THEMESPEC_H */
[themespec]
; Format and options of this themespec file:
options = "+themespec3 +Freeciv.Devel.2005.Nov.25"
; A simple name for the themespec specified by this file:
name = "deluxe"
priority = 20
; TODO: add more overall information fields on tiles,
; eg, description, authors, colors, etc.
; These are special because they get freed and reloaded
; as required:
main_intro_file = "misc/intro"
minimap_intro_file = "misc/radar"
; Below, the graphics spec files; must be somewhere (anywhere) in
; the data path. Order may be important for color allocation on
; low-color systems, and if there are any duplicate tags (lattermost
; tag is used).
files =
"deluxe/sdl/city.spec",
"deluxe/sdl/city_fist.spec",
"deluxe/sdl/cursors_anim.spec",
"deluxe/sdl/dip_icons.spec",
"deluxe/sdl/logo.spec",
"deluxe/sdl/small_theme_buttons.spec",
"deluxe/sdl/techs.spec",
"deluxe/sdl/tech_tree.spec",
"deluxe/sdl/theme_scrolls.spec",
"deluxe/sdl/theme_buttons.spec",
"deluxe/sdl/theme_boxs.spec",
"deluxe/sdl/theme_orders_buttons.spec",
"deluxe/icons.spec"
; Include color definitions
*include "misc/colors.tilespec"
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client,
Christian Prochaska <=
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Jason Short, 2005/12/01
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Christian Prochaska, 2005/12/01
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Jason Short, 2005/12/01
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Christian Prochaska, 2005/12/03
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Jason Short, 2005/12/03
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Daniel Markstedt, 2005/12/03
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Christian Prochaska, 2005/12/03
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Daniel Markstedt, 2005/12/03
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Jason Short, 2005/12/04
- [Freeciv-Dev] Re: (PR#14639) Patch for SDL client, Jason Short, 2005/12/04
|
|