Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2003:
[Freeciv-Dev] Re: (PR#3457) New sprite loading
Home

[Freeciv-Dev] Re: (PR#3457) New sprite loading

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients:;
Subject: [Freeciv-Dev] Re: (PR#3457) New sprite loading
From: "Raimar Falke" <rf13@xxxxxxxxxxxxxxxxx>
Date: Mon, 17 Mar 2003 00:11:27 -0800
Reply-to: rt@xxxxxxxxxxxxxx

On Fri, Mar 14, 2003 at 08:16:19PM -0800, Jason Short wrote:
> 
> [rfalke - Tue Mar  4 21:40:01 2003]:
> 
> > On Mon, Feb 17, 2003 at 11:58:54AM -0800, Raimar Falke wrote:
> > > 
> > > 
> > > I wrote a nice explanation of the patch but than I hit 'q' in the mail
> > > reader. ARGL. So here is the very short version:
> > > 
> > > Problems:
> > >  - special case for not permanently loaded/used graphics needed
> > >  - gui-sdl need sprites which other guis doesn't want to load
> > > 
> > > Solution has three functions load_sprite, unload_sprite and
> > > finish_loading. See code for usage.
> > 
> > I have applied the Jason's requests.
> > 
> > I have removed the sample user (intro/radar graphics). I have thought
> > about it and all the solution doesn't seem very nice. First problem
> > here is that I want a load_intro_radar_sprites which belongs to
> > free_intro_radar_sprites. Both can be made gui-independent. Problem
> > here is that load_intro_radar_sprites is called from gui code while
> > free_intro_radar_sprites is called from common code.
> 
> Good plan.
> 
> Now when I run the client (gui-gtk-2.0) I simply get:
> 
> (civclient:7399): GdkPixbuf-CRITICAL **: file gdk-pixbuf-io.c: line 729
> (gdk_pixbuf_new_from_file): assertion `filename != NULL' failed
> 0: Failed reading XPM file: (null)
> 
> and the client exits.  This is because main_intro_filename is NULL.

Uhh. Bad. My fault. Try this version.

        Raimar

-- 
 email: rf13@xxxxxxxxxxxxxxxxx
    1) Customers cause problems.
    2) Marketing is trying to create more customers.
  Therefore:
    3) Marketing is evil.

Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.111
diff -u -u -r1.111 tilespec.c
--- client/tilespec.c   2003/03/05 08:56:07     1.111
+++ client/tilespec.c   2003/03/17 08:02:54
@@ -79,15 +79,57 @@
 static bool is_mountainous = FALSE;
 static int roadstyle;
 
-static int num_spec_files = 0;
-static char **spec_filenames;  /* full pathnames */
+struct specfile;
 
-static struct hash_table *sprite_hash = NULL;
+#define SPECLIST_TAG specfile
+#define SPECLIST_TYPE struct specfile
+#include "speclist.h"
+
+#define SPECLIST_TAG specfile
+#define SPECLIST_TYPE struct specfile
+#include "speclist_c.h"
+
+#define specfile_list_iterate(list, pitem) \
+    TYPED_LIST_ITERATE(struct specfile, list, pitem)
+#define specfile_list_iterate_end  LIST_ITERATE_END
+
+struct small_sprite;
+#define SPECLIST_TAG small_sprite
+#define SPECLIST_TYPE struct small_sprite
+#include "speclist.h"
+
+#define SPECLIST_TAG small_sprite
+#define SPECLIST_TYPE struct small_sprite
+#include "speclist_c.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
+
+static struct specfile_list specfiles;
+
+struct specfile {
+  struct Sprite *big_sprite;
+  char *file_name;
+  struct small_sprite_list small_sprites;
+};
+
+/* 
+ * Information about an induvial sprite. All fields except sprite are
+ * filled at the time of the scan of the specfile. Sprite is
+ * set/cleared in load_sprite/unload_sprite on demand.
+ */
+struct small_sprite {
+  int ref_count;
+  int x, y, width, height;
+  struct specfile *sf;
+  struct Sprite *sprite;
+};
+
 /*
-   This hash table sprite_hash maps tilespec tags to tile Sprite pointers.
-   This is kept permanently after setup, for doing lookups on ruleset
-   information (including on reconnections etc).
-*/
+ * This hash table maps tilespec tags to struct small_sprites.
+ */
+static struct hash_table *sprite_hash = NULL;
 
 #define TILESPEC_CAPSTR "+tilespec2 duplicates_ok roadstyle"
 /*
@@ -212,35 +254,6 @@
 }
 
 /**********************************************************************
-  Returns the correct name of the gfx file (with path and extension)
-  Must be free'd when no longer used
-***********************************************************************/
-static char *tilespec_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);
-}
-
-/**********************************************************************
   Frees the tilespec toplevel data, in preparation for re-reading it.
 
   See tilespec_read_toplevel().
@@ -374,6 +387,163 @@
 }
 
 /**********************************************************************
+  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, *gfx_current_fileext, **gfx_fileexts;
+
+  if (sf->big_sprite) {
+    return;
+  }
+
+  if (!section_file_load(file, sf->file_name)) {
+    freelog(LOG_FATAL, _("Could not open \"%s\"."), sf->file_name);
+    exit(EXIT_FAILURE);
+  }
+  check_tilespec_capabilities(file, "spec", SPEC_CAPSTR, sf->file_name);
+
+  gfx_fileexts = gfx_fileextensions();
+  gfx_filename = secfile_lookup_str(file, "file.gfx");
+
+  while (!sf->big_sprite && (gfx_current_fileext = *gfx_fileexts++)) {
+    char *real_full_name;
+    char *full_name =
+       fc_malloc(strlen(gfx_filename) + strlen(gfx_current_fileext) + 2);
+    sprintf(full_name, "%s.%s", gfx_filename, gfx_current_fileext);
+
+    if ((real_full_name = datafilename(full_name))) {
+      freelog(LOG_DEBUG, "trying to load gfx file %s", real_full_name);
+      sf->big_sprite = load_gfxfile(real_full_name);
+      if (!sf->big_sprite) {
+       freelog(LOG_VERBOSE, "loading the gfx file %s failed",
+               real_full_name);
+      }
+    }
+    free(full_name);
+  }
+
+  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 specfile *sf)
+{
+  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);
+  }
+  check_tilespec_capabilities(file, "spec", SPEC_CAPSTR, sf->file_name);
+
+  (void) section_file_lookup(file, "info.artists"); /* currently unused */
+  (void)secfile_lookup_str(file, "file.gfx");
+
+  gridnames = secfile_get_secnames_prefix(file, "grid_", &num_grids);
+  if (num_grids == 0) {
+    freelog(LOG_FATAL, "spec %s has no grid_* sections", sf->file_name);
+    exit(EXIT_FAILURE);
+  }
+
+  for (i = 0; i < num_grids; i++) {
+    int j, k;
+    int x_top_left, y_top_left, dx, dy;
+    bool is_pixel_border =
+      secfile_lookup_bool_default(file, FALSE, "%s.is_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;
+
+      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);
+
+      /* there must be at least 1 because of the while(): */
+      assert(num_tags > 0);
+
+      x1 = x_top_left + column * dx + (is_pixel_border ? column : 0);
+      y1 = y_top_left + row * dy + (is_pixel_border ? row : 0);
+
+      ss->ref_count = 0;
+      ss->x = x1;
+      ss->y = y1;
+      ss->width = dx;
+      ss->height = dy;
+      ss->sf = sf;
+      ss->sprite = NULL;
+
+      small_sprite_list_insert(&ss->sf->small_sprites, ss);
+
+      for (k = 0; k < num_tags; k++) {
+       if (!hash_insert(sprite_hash, mystrdup(tags[k]), ss)) {
+         freelog(LOG_ERROR, "warning: already have a sprite for %s",
+                 tags[k]);
+       }
+      }
+      free(tags);
+      tags = NULL;
+    }
+  }
+  free(gridnames);
+  gridnames = NULL;
+
+  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 *tilespec_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);
+}
+
+/**********************************************************************
   Finds and reads the toplevel tilespec file based on given name.
   Sets global variables, including tile sizes and full names for
   intro files.
@@ -383,6 +553,8 @@
   struct section_file the_file, *file = &the_file;
   char *fname, *c;
   int i;
+  int num_spec_files;
+  char **spec_filenames;
 
   fname = tilespec_fullname(tileset_name);
   freelog(LOG_VERBOSE, "tilespec file is %s", fname);
@@ -458,10 +630,20 @@
     freelog(LOG_FATAL, "No tile files specified in \"%s\"", fname);
     exit(EXIT_FAILURE);
   }
-  
-  for(i=0; i<num_spec_files; i++) {
-    spec_filenames[i] = mystrdup(datafilename_required(spec_filenames[i]));
+
+  sprite_hash = hash_new(hash_fval_string, hash_fcmp_string);
+  specfile_list_init(&specfiles);
+  for (i = 0; i < num_spec_files; i++) {
+    struct specfile *sf = fc_malloc(sizeof(*sf));
+
     freelog(LOG_DEBUG, "spec file %s", spec_filenames[i]);
+    
+    sf->big_sprite = NULL;
+    sf->file_name = mystrdup(datafilename_required(spec_filenames[i]));
+    small_sprite_list_init(&sf->small_sprites);
+    scan_specfile(sf);
+
+    specfile_list_insert(&specfiles, sf);
   }
 
   section_file_check_unused(file, fname);
@@ -472,105 +654,6 @@
 }
 
 /**********************************************************************
-  Load one specfile and specified xpm file; splits xpm into tiles,
-  and save sprites in sprite_hash.
-***********************************************************************/
-static void tilespec_load_one(const char *spec_filename)
-{
-  struct section_file the_file, *file = &the_file;
-  struct Sprite *big_sprite = NULL, *small_sprite;
-  const char *gfx_filename, *gfx_current_fileext, **gfx_fileexts;
-  char **gridnames, **tags;
-  int num_grids, num_tags;
-  int i, j, k;
-  int x_top_left, y_top_left, dx, dy;
-  int row, column;
-  int x1, y1;
-
-  freelog(LOG_DEBUG, "loading spec %s", spec_filename);
-  if (!section_file_load(file, spec_filename)) {
-    freelog(LOG_FATAL, _("Could not open \"%s\"."), spec_filename);
-    exit(EXIT_FAILURE);
-  }
-  check_tilespec_capabilities(file, "spec", SPEC_CAPSTR, spec_filename);
-
-  (void) section_file_lookup(file, "info.artists"); /* currently unused */
-
-  gfx_fileexts = gfx_fileextensions();
-  gfx_filename = secfile_lookup_str(file, "file.gfx");
-
-  while((!big_sprite) && (gfx_current_fileext = *gfx_fileexts++))
-  {
-    char *full_name,*real_full_name;
-    full_name = fc_malloc(strlen(gfx_filename)+strlen(gfx_current_fileext)+2);
-    sprintf(full_name,"%s.%s",gfx_filename,gfx_current_fileext);
-
-    if((real_full_name = datafilename(full_name)))
-    {
-      freelog(LOG_DEBUG, "trying to load gfx file %s", real_full_name);
-      if(!(big_sprite = load_gfxfile(real_full_name)))
-      {
-        freelog(LOG_VERBOSE, "loading the gfx file %s failed", real_full_name);
-      }
-    }
-    free(full_name);
-  }
-
-  if(!big_sprite) {
-    freelog(LOG_FATAL, _("Couldn't load gfx file for the spec file %s"),
-           spec_filename);
-    exit(EXIT_FAILURE);
-  }
-
-
-  gridnames = secfile_get_secnames_prefix(file, "grid_", &num_grids);
-  if (num_grids==0) {
-    freelog(LOG_FATAL, "spec %s has no grid_* sections", spec_filename);
-    exit(EXIT_FAILURE);
-  }
-
-  for(i=0; i<num_grids; i++) {
-    bool is_pixel_border =
-      secfile_lookup_bool_default(file, FALSE, "%s.is_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)) {
-      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);
-
-      /* there must be at least 1 because of the while(): */
-      assert(num_tags>0);
-
-      x1 = x_top_left + column * dx + (is_pixel_border ? column : 0);
-      y1 = y_top_left + row * dy + (is_pixel_border ? row : 0);
-      small_sprite = crop_sprite(big_sprite, x1, y1, dx, dy);
-
-      for(k=0; k<num_tags; k++) {
-       /* don't free old sprite, since could be multiple tags
-          pointing to it */
-       (void) hash_replace(sprite_hash, mystrdup(tags[k]), small_sprite);
-      }
-      free(tags);
-      tags = NULL;
-    }
-  }
-  free(gridnames);
-  gridnames = NULL;
-
-  free_sprite(big_sprite);
-  
-  section_file_check_unused(file, spec_filename);
-  section_file_free(file);
-  freelog(LOG_DEBUG, "finished %s", spec_filename);
-}
-
-/**********************************************************************
   Returns a text name for the citizen, as used in the tileset.
 ***********************************************************************/
 static const char *get_citizen_name(enum citizen_type citizen)
@@ -613,7 +696,7 @@
      
 /* Not very safe, but convenient: */
 #define SET_SPRITE(field, tag) do { \
-       sprites.field = hash_lookup_data(sprite_hash, tag);\
+       sprites.field = load_sprite(tag);\
        if (!sprites.field) {                              \
          die("Sprite tag %s missing.", buffer);           \
        }                                                  \
@@ -621,9 +704,9 @@
 
 /* Sets sprites.field to tag or (if tag isn't available) to alt */
 #define SET_SPRITE_ALT(field, tag, alt) do {                   \
-       sprites.field = hash_lookup_data(sprite_hash, tag);     \
+       sprites.field = load_sprite(tag);     \
        if (!sprites.field) {                                   \
-           sprites.field = hash_lookup_data(sprite_hash, alt); \
+           sprites.field = load_sprite(alt); \
        }                                                       \
        if (!sprites.field) {                                   \
          die("Sprite tag %s and alternate %s are both missing.", tag, alt); \
@@ -632,7 +715,7 @@
 
 /* Sets sprites.field to tag, or NULL if not available */
 #define SET_SPRITE_OPT(field, tag) \
-  sprites.field = hash_lookup_data(sprite_hash, tag)
+  sprites.field = load_sprite(tag)
 
 /**********************************************************************
   Initialize 'sprites' structure based on hardwired tags which
@@ -668,7 +751,7 @@
   /* Load the citizen sprite graphics. */
   for (i = 0; i < NUM_TILES_CITIZEN; i++) {
     my_snprintf(buffer, sizeof(buffer), "citizen.%s", get_citizen_name(i));
-    sprites.citizen[i].sprite[0] = hash_lookup_data(sprite_hash, buffer);
+    sprites.citizen[i].sprite[0] = load_sprite(buffer);
     if (sprites.citizen[i].sprite[0]) {
       /*
        * If this form exists, use it as the only sprite.  This allows
@@ -682,7 +765,7 @@
     for (j = 0; j < NUM_TILES_CITIZEN; j++) {
       my_snprintf(buffer, sizeof(buffer), "citizen.%s_%d",
                  get_citizen_name(i), j);
-      sprites.citizen[i].sprite[j] = hash_lookup_data(sprite_hash, buffer);
+      sprites.citizen[i].sprite[j] = load_sprite(buffer);
       if (!sprites.citizen[i].sprite[j]) {
        break;
       }
@@ -766,7 +849,7 @@
   do {
     my_snprintf(buffer, sizeof(buffer), "explode.unit_%d",
                num_tiles_explode_unit++);
-  } while (hash_key_exists(sprite_hash, buffer));
+  } while (load_sprite(buffer));
   num_tiles_explode_unit--;
     
   if (num_tiles_explode_unit==0) {
@@ -907,16 +990,8 @@
 ***********************************************************************/
 void tilespec_load_tiles(void)
 {
-  int i;
-  
-  assert(num_spec_files>0);
-  sprite_hash = hash_new(hash_fval_string, hash_fcmp_string);
-
-  for(i=0; i<num_spec_files; i++) {
-    tilespec_load_one(spec_filenames[i]);
-  }
-
   tilespec_lookup_sprite_tags();
+  finish_loading_sprites();
 }
 
 /**********************************************************************
@@ -935,10 +1010,10 @@
     die("attempt to lookup for %s %s before sprite_hash setup", what, name);
   }
 
-  sp = hash_lookup_data(sprite_hash, tag);
+  sp = load_sprite(tag);
   if (sp) return sp;
 
-  sp = hash_lookup_data(sprite_hash, alt);
+  sp = load_sprite(alt);
   if (sp) {
     freelog(loglevel, "Using alternate graphic %s (instead of %s) for %s %s",
            alt, tag, what, name);
@@ -2119,10 +2194,10 @@
 
   for(j=0; j<32 && city_styles[style].tiles_num < MAX_CITY_TILES; j++) {
     my_snprintf(buffer, sizeof(buffer), "%s_%d", graphics, j);
-    sp = hash_lookup_data(sprite_hash, buffer);
+    sp = load_sprite(buffer);
     if (is_isometric) {
       my_snprintf(buffer, sizeof(buffer_wall), "%s_%d_wall", graphics, j);
-      sp_wall = hash_lookup_data(sprite_hash, buffer);
+      sp_wall = load_sprite(buffer);
     }
     if (sp) {
       sprites.city.tile[style][city_styles[style].tiles_num] = sp;
@@ -2142,7 +2217,7 @@
   if (!is_isometric) {
     /* the wall tile */
     my_snprintf(buffer, sizeof(buffer), "%s_wall", graphics);
-    sp = hash_lookup_data(sprite_hash, buffer);
+    sp = load_sprite(buffer);
     if (sp) {
       sprites.city.tile[style][city_styles[style].tiles_num] = sp;
     } else {
@@ -2152,7 +2227,7 @@
 
   /* occupied tile */
   my_snprintf(buffer, sizeof(buffer), "%s_occupied", graphics);
-  sp = hash_lookup_data(sprite_hash, buffer);
+  sp = load_sprite(buffer);
   if (sp) {
     sprites.city.tile[style][city_styles[style].tiles_num+1] = sp;
   } else {
@@ -2183,12 +2258,9 @@
            "No tiles for alternate %s style, using default tiles",
             city_styles[style].graphic_alt);
 
-    sprites.city.tile[style][0] =
-      hash_lookup_data(sprite_hash, "cd.city");
-    sprites.city.tile[style][1] =
-      hash_lookup_data(sprite_hash, "cd.city_wall");
-    sprites.city.tile[style][2] =
-      hash_lookup_data(sprite_hash, "cd.occupied");
+    sprites.city.tile[style][0] = load_sprite("cd.city");
+    sprites.city.tile[style][1] = load_sprite("cd.city_wall");
+    sprites.city.tile[style][2] = load_sprite("cd.occupied");
     city_styles[style].tiles_num = 1;
     city_styles[style].tresh[0] = 0;
   }
@@ -2321,24 +2393,18 @@
     return NULL;
 }
 
-/**********************************************************************
-...
-***********************************************************************/
-static int my_cmp(const void *a1, const void *a2)
+static void unload_all_sprites(void )
 {
-  const struct Sprite **b1 = (const struct Sprite **) a1;
-  const struct Sprite **b2 = (const struct Sprite **) a2;
+  int i, entries = hash_num_entries(sprite_hash);
 
-  const struct Sprite *c1 = *b1;
-  const struct Sprite *c2 = *b2;
+  for (i = 0; i < entries; i++) {
+    const char *tag_name = hash_key_by_number(sprite_hash, i);
+    struct small_sprite *ss = hash_lookup_data(sprite_hash, tag_name);
 
-  if (c1 < c2) {
-    return -1;
-  }
-  if (c2 < c1) {
-    return 1;
+    while (ss->ref_count > 0) {
+      unload_sprite(tag_name);
+    }
   }
-  return 0;
 }
 
 /**********************************************************************
@@ -2347,33 +2413,35 @@
 void tilespec_free_tiles(void)
 {
   int i, entries = hash_num_entries(sprite_hash);
-  struct Sprite **sprites = fc_malloc(sizeof(*sprites) * entries);
-  struct Sprite *last_sprite = NULL;
 
   freelog(LOG_DEBUG, "tilespec_free_tiles");
 
+  unload_all_sprites();
+
   for (i = 0; i < entries; i++) {
     const char *key = hash_key_by_number(sprite_hash, 0);
 
-    sprites[i] = hash_delete_entry(sprite_hash, key);
+    hash_delete_entry(sprite_hash, key);
     free((void *) key);
   }
 
-  qsort(sprites, entries, sizeof(sprites[0]), my_cmp);
-
-  for (i = 0; i < entries; i++) {
-    if (sprites[i] == last_sprite) {
-      continue;
-    }
-
-    last_sprite = sprites[i];
-    free_sprite(sprites[i]);
-  }
-
-  free(sprites);
-
   hash_free(sprite_hash);
   sprite_hash = NULL;
+
+  specfile_list_iterate(specfiles, sf) {
+    small_sprite_list_iterate(sf->small_sprites, ss) {
+      small_sprite_list_unlink(&sf->small_sprites, ss);
+      assert(ss->sprite == NULL);
+      free(ss);
+    } small_sprite_list_iterate_end;
+
+    specfile_list_unlink(&specfiles, sf);
+    if (sf->big_sprite) {
+      free_sprite(sf->big_sprite);
+      sf->big_sprite = NULL;
+    }
+    free(sf);
+  } specfile_list_iterate_end;
 }
 
 /**************************************************************************
@@ -2389,4 +2457,67 @@
   assert(type >= 0 && type < NUM_TILES_CITIZEN);
   citizen_index %= sprites.citizen[type].count;
   return sprites.citizen[type].sprite[citizen_index];
+}
+
+/**************************************************************************
+  Loads the sprite. If the sprite is already loaded a reference
+  counter is increased. Can return NULL if the sprite couldn't be
+  loaded.
+**************************************************************************/
+struct Sprite *load_sprite(const char *tag_name)
+{
+  struct small_sprite *ss = hash_lookup_data(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) {
+    assert(ss->ref_count == 0);
+    ensure_big_sprite(ss->sf);
+    ss->sprite =
+       crop_sprite(ss->sf->big_sprite, ss->x, ss->y, ss->width, ss->height);
+  }
+
+  ss->ref_count++;
+  return ss->sprite;
+}
+
+/**************************************************************************
+  Unloads the sprite. Decrease the reference counter. If the last
+  reference is removed the sprite is freed.
+**************************************************************************/
+void unload_sprite(const char *tag_name)
+{
+  struct small_sprite *ss = hash_lookup_data(sprite_hash, tag_name);
+
+  assert(ss);
+  assert(ss->ref_count >= 1);
+  assert(ss->sprite);
+
+  ss->ref_count--;
+
+  if (ss->ref_count == 0) {
+    freelog(LOG_DEBUG, "freeing sprite '%s'", tag_name);
+    free_sprite(ss->sprite);
+    ss->sprite = NULL;
+  }
+}
+
+/**************************************************************************
+  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.
+**************************************************************************/
+void finish_loading_sprites(void)
+{
+  specfile_list_iterate(specfiles, sf) {
+    if (sf->big_sprite) {
+      free_sprite(sf->big_sprite);
+      sf->big_sprite = NULL;
+    }
+  } specfile_list_iterate_end;
 }
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.38
diff -u -u -r1.38 tilespec.h
--- client/tilespec.h   2003/02/02 00:15:52     1.38
+++ client/tilespec.h   2003/03/17 08:02:54
@@ -261,4 +261,8 @@
 
 extern int num_tiles_explode_unit;
 
+struct Sprite *load_sprite(const char *tag_name);
+void unload_sprite(const char *tag_name);
+void finish_loading_sprites(void);
+
 #endif  /* FC__TILESPEC_H */
Index: client/gui-gtk-2.0/graphics.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/graphics.c,v
retrieving revision 1.13
diff -u -u -r1.13 graphics.c
--- client/gui-gtk-2.0/graphics.c       2003/02/26 23:49:49     1.13
+++ client/gui-gtk-2.0/graphics.c       2003/03/17 08:02:56
@@ -309,6 +309,7 @@
 
   if (!(im = gdk_pixbuf_new_from_file(filename, NULL))) {
     freelog(LOG_FATAL, "Failed reading XPM file: %s", filename);
+    assert(0);
     exit(EXIT_FAILURE);
   }
 

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