Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2005:
[Freeciv-Dev] Re: (PR#14639) Patch for SDL client
Home

[Freeciv-Dev] Re: (PR#14639) Patch for SDL client

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#14639) Patch for SDL client
From: "Christian Prochaska" <cp.ml.freeciv.dev@xxxxxxxxxxxxxx>
Date: Thu, 1 Dec 2005 18:54:15 -0800
Reply-to: bugs@xxxxxxxxxxx

<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"

[Prev in Thread] Current Thread [Next in Thread]