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: Fri, 9 Dec 2005 20:54:03 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14639 >

This transaction appears to have no content
Here's the finished theme handling patch. Themes for the SDL client are
expected in "themes/gui-sdl" within any of the freeciv data directories and
they are recognized by the "theme.themespec" file (example:
"/~/.freeciv/themes/gui-sdl/deluxe/theme.themespec"). The repackaged
"deluxe" theme can be found here:

http://web.inf.tu-dresden.de/~s3564543/freeciv-sdl/deluxe_theme.tar.bz2
<http://web.inf.tu-dresden.de/%7Es3564543/freeciv-sdl/deluxe_theme.tar.bz2>

Using some of aneglus' graphics seems a good idea.

The diplomacy icons, if we have to replace them, should maybe look more like
the ones currently used. Here's a screenshot showing the diplomacy dialog in
action:

http://rcswww.urz.tu-dresden.de/~s3564543/freeciv-sdl/diplodlg.png<http://rcswww.urz.tu-dresden.de/%7Es3564543/freeciv-sdl/diplodlg.png>

Red seals fit perfectly, don't they?
Here's the finished theme handling patch. Themes for the SDL client are expected in "themes/gui-sdl" within any of the freeciv data directories and they are recognized by the "theme.themespec" file (example: "/~/.freeciv/themes/gui-sdl/deluxe/theme.themespec"). The repackaged "deluxe" theme can be found here:

http://web.inf.tu-dresden.de/~s3564543/freeciv-sdl/deluxe_theme.tar.bz2

Using some of aneglus' graphics seems a good idea.

The diplomacy icons, if we have to replace them, should maybe look more like the ones currently used. Here's a screenshot showing the diplomacy dialog in action:

http://rcswww.urz.tu-dresden.de/~s3564543/freeciv-sdl/diplodlg.png

Red seals fit perfectly, don't they?
diff -urN -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-04 05:59:27.000000000 
+0100
+++ devel/client/gui-sdl/graphics.c     2005-12-09 17:25:19.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 -urN -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-04 05:59:27.000000000 
+0100
+++ devel/client/gui-sdl/gui_main.c     2005-12-09 23:58:06.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,12 +939,8 @@
                        CF_DRAW_PLAYERS_PEACE_STATUS|
                        CF_DRAW_PLAYERS_ALLIANCE_STATUS);
   
-  tilespec_setup_theme();
-  tilespec_setup_anim();
-  tilespec_setup_city_gfx();
-  tilespec_setup_city_icons();
-
   tileset_load_tiles(tileset);
+  tileset_use_prefered_theme(tileset);
       
   load_cursors();  
 
@@ -1015,11 +1012,14 @@
   #endif
 
   free_auxiliary_tech_icons();
-  
-  tilespec_free_anim();
   unload_cursors();
-  free_font_system();
   
+/* FIXME: the font system cannot be freed yet, because it is still 
+ * needed in civclient.c for message window output */
+#if 0 
+  free_font_system();
+#endif  
+
   quit_sdl();
 }
 
diff -urN -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-04 
05:59:27.000000000 +0100
+++ devel/client/gui-sdl/gui_tilespec.c 2005-12-09 17:25:19.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,12 +207,10 @@
   ...
 **************************************************************************/
 void tilespec_setup_city_gfx(void) {
-  struct sprite *pSpr = lookup_sprite_tag_alt(tileset, "theme.city", "", TRUE, 
"", "");    
-  pCity_Surf = (pSpr ? GET_SURF(pSpr) : NULL);
-  
-  #ifdef SMALL_SCREEN
-  pCity_Surf = ZoomSurface(pCity_Surf, 0.5, 0.5, 0);
-  #endif
+  struct sprite *pSpr = themeset_lookup_sprite_tag_alt(
+                                    themeset, "theme.city", "", TRUE, "", ""); 
   
+
+  pCity_Surf = (pSpr ? adj_surf(GET_SURF(pSpr)) : NULL);
   
   assert(pCity_Surf != NULL);
 }
@@ -343,7 +337,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 +485,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 +494,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) {    \
@@ -568,7 +562,7 @@
 /*
  *     Free memmory
  */
-void tilespec_unload_theme(void)
+void tilespec_free_theme(void)
 {
   FREESURFACE(pTheme->Button);
   FREESURFACE(pTheme->Edit);
diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_tilespec.h 
devel/client/gui-sdl/gui_tilespec.h
--- devel_distclean/client/gui-sdl/gui_tilespec.h       2005-12-04 
05:59:27.000000000 +0100
+++ devel/client/gui-sdl/gui_tilespec.h 2005-12-09 17:25:19.000000000 +0100
@@ -22,6 +22,8 @@
 #ifndef FC__GUI_TILESPEC_H
 #define FC__GUI_TILESPEC_H
 
+#include <SDL/SDL.h>
+
 #include "citydlg_common.h"    /* struct citizen_type */
   
 struct Theme {
@@ -128,7 +130,7 @@
 } *pTheme;
 
 void tilespec_setup_theme(void);
-void tilespec_unload_theme(void);
+void tilespec_free_theme(void);
 
 enum DirScrolling {
   SCROLL_NORTH = 0,
@@ -202,8 +204,10 @@
 SDL_Surface *pCity_Surf;
 
 void tilespec_setup_city_gfx(void);
+  
 void tilespec_setup_city_icons(void);
 void tilespec_free_city_icons(void);
+
 void reload_citizens_icons(int style);
 SDL_Surface * get_city_gfx(void);
 SDL_Surface * get_citizen_surface(enum citizen_category type,
diff -urN -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-27 01:35:27.000000000 
+0100
+++ devel/client/gui-sdl/Makefile.am    2005-12-09 17:25:19.000000000 +0100
@@ -82,6 +82,7 @@
        spaceshipdlg.c  \
        spaceshipdlg.h  \
        themes.c        \
+       themespec.c     \
        wldlg.c         \
        wldlg.h         \
        gui_string.c    \
diff -urN -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-09 17:25:19.000000000 +0100
@@ -0,0 +1,1054 @@
+/********************************************************************** 
+ 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"
+
+#include "gui_tilespec.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 themeset *themeset;
+
+#define THEMESPEC_CAPSTR "+themespec3 duplicates_ok"
+/*
+ * Themespec 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);*/
+    dname = 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;
+  }
+}
+
+/**************************************************************************
+  Clean up.
+**************************************************************************/
+void themeset_free(struct themeset *t)
+{
+  if (t) {
+    themeset_free_tiles(t);
+    themeset_free_toplevel(t);
+    specfile_list_free(t->specfiles);
+    small_sprite_list_free(t->small_sprites);
+    free(t);
+    t = NULL;
+  }
+}
+
+/**********************************************************************
+  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",
+                                  THEMESPEC_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, "theme 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, "theme 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 theme graphics 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);
+
+  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;
+  }
+}
+
+/* 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 the
+  client always requires. 
+***********************************************************************/
+static void themeset_lookup_sprite_tags(struct themeset *t)
+{
+  /* the 'sprites' structure is currently not used, for now we call some
+   * functions in gui_tilespec.c instead */
+  
+  tilespec_setup_theme();
+  tilespec_setup_anim();
+  tilespec_setup_city_gfx();
+  tilespec_setup_city_icons();
+}
+
+/**************************************************************************
+  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  
+  
+  /* the 'sprites' structure is currently not used, for now we call some
+   * functions in gui_tilespec.c instead */
+
+  tilespec_free_theme();
+  tilespec_free_anim();
+  tilespec_free_city_icons();
+}
diff -urN -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-09 17:25:19.000000000 +0100
@@ -0,0 +1,52 @@
+/********************************************************************** 
+ 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);
+
+/* Themeset 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);
+
+#endif  /* FC__THEMESPEC_H */
diff -urN -X devel/diff_ignore devel_distclean/utility/shared.c 
devel/utility/shared.c
--- devel_distclean/utility/shared.c    2005-11-27 00:54:02.000000000 +0100
+++ devel/utility/shared.c      2005-12-10 01:51:14.000000000 +0100
@@ -833,7 +833,7 @@
 
   num_dirs, if not NULL, will be set to the number of entries in the list.
 ***************************************************************************/
-static const char **get_data_dirs(int *num_dirs)
+const char **get_data_dirs(int *num_dirs)
 {
   const char *path;
   char *path2, *tok;
diff -urN -X devel/diff_ignore devel_distclean/utility/shared.h 
devel/utility/shared.h
--- devel_distclean/utility/shared.h    2005-11-27 01:35:10.000000000 +0100
+++ devel/utility/shared.h      2005-12-10 01:51:07.000000000 +0100
@@ -186,6 +186,9 @@
                                                                                
 char *user_home_dir(void);
 char *user_username(char *buf, size_t bufsz);
+  
+const char **get_data_dirs(int *num_dirs);
+  
 char **datafilelist(const char *suffix);
 struct datafile_list *datafilelist_infix(const char *subpath,
                                          const char *infix, bool nodups);
@@ -232,4 +235,3 @@
 
 char scanin(char **buf, char *delimiters, char *dest, int size);
 #endif  /* FC__SHARED_H */
-
diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/themes.c 
devel/client/gui-sdl/themes.c
--- devel_distclean/client/gui-sdl/themes.c     2005-12-04 05:59:27.000000000 
+0100
+++ devel/client/gui-sdl/themes.c       2005-12-10 02:42:19.000000000 +0100
@@ -14,15 +14,32 @@
 #include <config.h>
 #endif
 
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "shared.h"
+#include "themespec.h"
+
 #include "themes_common.h"
 #include "themes_g.h"
 
+#define GUI_SDL_DEFAULT_THEME "deluxe"
+
 /*****************************************************************************
-  Loads a gtk theme directory/theme_name
+  Loads a gui-sdl theme directory/theme_name
 *****************************************************************************/
 void gui_load_theme(const char *directory, const char *theme_name)
 {
-  /* Nothing */
+  char buf[strlen(directory) + strlen("/") + strlen(theme_name) + 
strlen("/theme") + 1];
+  
+  /* free previous loaded theme, if any */
+  themeset_free(themeset);
+  
+  my_snprintf(buf, sizeof(buf), "%s/%s/theme", directory, theme_name);
+  
+  themespec_try_read(buf);
+  themeset_load_tiles(themeset);
 }
 
 /*****************************************************************************
@@ -30,7 +47,8 @@
 *****************************************************************************/
 void gui_clear_theme(void)
 {
-  /* Nothing */
+  themeset_free(themeset);
+  load_theme(GUI_SDL_DEFAULT_THEME);
 }
 
 /*****************************************************************************
@@ -41,19 +59,76 @@
 *****************************************************************************/
 char **get_gui_specific_themes_directories(int *count)
 {
-  *count = 0;
-  
-  return fc_malloc(sizeof(char*) * 0);
+  int i;
+
+  const char **data_directories = get_data_dirs(count);
+
+  char **directories = fc_malloc(sizeof(char *) * *count);  
+
+  for (i = 0; i < *count; i++) {
+    char buf[strlen(data_directories[i]) + strlen("/themes/gui-sdl") + 1];
+    
+    my_snprintf(buf, sizeof(buf), "%s/themes/gui-sdl", data_directories[i]);
+
+    directories[i] = mystrdup(buf);
+  }
+
+  return directories;
 }
 
 /*****************************************************************************
   Return an array of names of usable themes in the given directory.
   Array size is stored in count.
-  Useable theme for gtk+ is a directory which contains file gtk-2.0/gtkrc.
+  Useable theme for gui-sdl is a directory which contains file theme.themespec.
   The caller is responsible for freeing the array and the names
 *****************************************************************************/
 char **get_useable_themes_in_directory(const char *directory, int *count)
 {
+  DIR *dir;
+  struct dirent *entry;
+  
+  char **theme_names = fc_malloc(sizeof(char *) * 2);
+  /* Allocated memory size */
+  int t_size = 2;
+
   *count = 0;
-  return fc_malloc(sizeof(char*) * 0);
+
+  dir = opendir(directory);
+  if (!dir) {
+    /* This isn't directory or we can't list it */
+    return theme_names;
+  }
+
+  while ((entry = readdir(dir))) {
+    char buf[strlen(directory) + strlen(entry->d_name) + 32];
+    struct stat stat_result;
+
+    my_snprintf(buf, sizeof(buf),
+               "%s/%s/theme.themespec", directory, entry->d_name);
+    
+    if (stat(buf, &stat_result) != 0) {
+      /* File doesn't exist */
+      continue;
+    }
+    
+    if (!S_ISREG(stat_result.st_mode)) {
+      /* Not a regular file */
+      continue;
+    }
+    
+    /* Otherwise it's ok */
+
+    /* Increase array size if needed */
+    if (*count == t_size) {
+      theme_names = fc_realloc(theme_names, t_size * 2 * sizeof(char *));
+      t_size *= 2;
+    }
+
+    theme_names[*count] = mystrdup(entry->d_name);
+    (*count)++;
+  }
+
+  closedir(dir);
+
+  return theme_names;
 }


























































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