(svn r3017) -NewGRF: Implement sprite group unreferencing and unloading.
authorpeter1138
Tue, 04 Oct 2005 21:42:00 +0000
changeset 2491 15a117630a4f
parent 2490 0eac698c33cf
child 2492 b4785c8f3700
(svn r3017) -NewGRF: Implement sprite group unreferencing and unloading.
engine.c
engine.h
newgrf.c
sprite.c
sprite.h
--- a/engine.c	Tue Oct 04 20:58:10 2005 +0000
+++ b/engine.c	Tue Oct 04 21:42:00 2005 +0000
@@ -294,6 +294,28 @@
 	return NULL;
 }
 
+/**
+ * Unload all wagon override sprite groups.
+ */
+void UnloadWagonOverrides(void)
+{
+	WagonOverrides *wos;
+	WagonOverride *wo;
+	EngineID engine;
+	int i;
+
+	for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
+		wos = &_engine_wagon_overrides[engine];
+		for (i = 0; i < wos->overrides_count; i++) {
+			wo = &wos->overrides[i];
+			UnloadSpriteGroup(&wo->group);
+			free(wo->train_id);
+		}
+		free(wos->overrides);
+		wos->overrides_count = 0;
+		wos->overrides = NULL;
+	}
+}
 
 // 0 - 28 are cargos, 29 is default, 30 is the advert (purchase list)
 // (It isn't and shouldn't be like this in the GRF files since new cargo types
@@ -303,13 +325,32 @@
 
 void SetCustomEngineSprites(EngineID engine, byte cargo, SpriteGroup *group)
 {
-	/* FIXME: If we are replacing an override, release original SpriteGroup
-	 * to prevent leaks. But first we need to refcount the SpriteGroup.
-	 * --pasky */
+	if (engine_custom_sprites[engine][cargo] != NULL) {
+		DEBUG(grf, 6)("SetCustomEngineSprites: engine `%d' cargo `%d' already has group -- removing.", engine, cargo);
+		UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]);
+	}
 	engine_custom_sprites[engine][cargo] = group;
 	group->ref_count++;
 }
 
+/**
+ * Unload all engine sprite groups.
+ */
+void UnloadCustomEngineSprites(void)
+{
+	EngineID engine;
+	CargoID cargo;
+
+	for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
+		for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) {
+			if (engine_custom_sprites[engine][cargo] != NULL) {
+				DEBUG(grf, 6)("UnloadCustomEngineSprites: Unloading group for engine `%d' cargo `%d'.", engine, cargo);
+				UnloadSpriteGroup(&engine_custom_sprites[engine][cargo]);
+			}
+		}
+	}
+}
+
 typedef SpriteGroup *(*resolve_callback)(const SpriteGroup *spritegroup,
 	const Vehicle *veh, uint16 callback_info, void *resolve_func); /* XXX data pointer used as function pointer */
 
--- a/engine.h	Tue Oct 04 20:58:10 2005 +0000
+++ b/engine.h	Tue Oct 04 21:42:00 2005 +0000
@@ -276,4 +276,7 @@
 	return &_road_vehicle_info[e - ROAD_ENGINES_INDEX];
 }
 
+void UnloadWagonOverrides(void);
+void UnloadCustomEngineSprites(void);
+
 #endif /* ENGINE_H */
--- a/newgrf.c	Tue Oct 04 20:58:10 2005 +0000
+++ b/newgrf.c	Tue Oct 04 21:42:00 2005 +0000
@@ -1404,6 +1404,8 @@
 		}
 		dg->default_group->ref_count++;
 
+		if (_cur_grffile->spritegroups[setid] != NULL)
+			UnloadSpriteGroup(&_cur_grffile->spritegroups[setid]);
 		_cur_grffile->spritegroups[setid] = group;
 		group->ref_count++;
 		return;
@@ -1454,6 +1456,8 @@
 			}
 		}
 
+		if (_cur_grffile->spritegroups[setid] != NULL)
+			UnloadSpriteGroup(&_cur_grffile->spritegroups[setid]);
 		_cur_grffile->spritegroups[setid] = group;
 		group->ref_count++;
 		return;
@@ -1519,6 +1523,8 @@
 		DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i]->g.result.result, spriteset_id);
 	}
 
+	if (_cur_grffile->spritegroups[setid] != NULL)
+		UnloadSpriteGroup(&_cur_grffile->spritegroups[setid]);
 	_cur_grffile->spritegroups[setid] = group;
 	group->ref_count++;
 }
@@ -2296,6 +2302,29 @@
 }
 
 /**
+ * Unload unused sprite groups from the specified GRF file.
+ * Called after loading each GRF file.
+ * @param file GRF file
+ */
+static void ReleaseSpriteGroups(GRFFile *file)
+{
+	int i;
+
+	// Bail out if no spritegroups were defined.
+	if (file->spritegroups == NULL)
+		return;
+
+	DEBUG(grf, 6)("ReleaseSpriteGroups: Releasing for `%s'.", file->filename);
+	for (i = 0; i < file->spritegroups_count; i++) {
+		if (file->spritegroups[i] != NULL)
+			UnloadSpriteGroup(&file->spritegroups[i]);
+	}
+	free(file->spritegroups);
+	file->spritegroups = NULL;
+	file->spritegroups_count = 0;
+}
+
+/**
  * Reset all NewGRF loaded data
  * TODO
  */
@@ -2321,6 +2350,10 @@
 		}
 	}
 	memcpy(&_bridge, &orig_bridge, sizeof(_bridge));
+
+	// Unload sprite group data
+	UnloadWagonOverrides();
+	UnloadCustomEngineSprites();
 }
 
 static void InitNewGRFFile(const char* filename, int sprite_offset)
@@ -2493,6 +2526,11 @@
 
 		if (_skip_sprites > 0) _skip_sprites--;
 	}
+
+	// Release our sprite group references.
+	// Any groups that are referenced elsewhere will be cleaned up later.
+	// This removes groups that aren't used. (Perhaps skipped?)
+	ReleaseSpriteGroups(_cur_grffile);
 }
 
 
--- a/sprite.c	Tue Oct 04 20:58:10 2005 +0000
+++ b/sprite.c	Tue Oct 04 21:42:00 2005 +0000
@@ -4,6 +4,7 @@
 #include "openttd.h"
 #include "sprite.h"
 #include "variables.h"
+#include "debug.h"
 
 
 SpriteGroup *EvalDeterministicSpriteGroup(const DeterministicSpriteGroup *dsg, int value)
@@ -96,3 +97,72 @@
 
 	return (rsg->num_groups - 1) << rsg->lowest_randbit;
 }
+
+/**
+ * Traverse a sprite group and release its and its child's memory.
+ * A group is only released if its reference count is zero.
+ * We pass a pointer to a pointer so that the original reference can be set to NULL.
+ * @param group_ptr Pointer to sprite group reference.
+ */
+void UnloadSpriteGroup(SpriteGroup **group_ptr)
+{
+	SpriteGroup *group;
+	int i;
+
+	assert(group_ptr != NULL);
+	assert(*group_ptr != NULL);
+
+	group = *group_ptr;
+	*group_ptr = NULL; // Remove this reference.
+
+	group->ref_count--;
+	if (group->ref_count > 0) {
+		DEBUG(grf, 6)("UnloadSpriteGroup: Group at `%p' (type %d) has %d reference(s) left.", group, group->type, group->ref_count);
+		return; // Still some references left, so don't clear up.
+	}
+
+	DEBUG(grf, 6)("UnloadSpriteGroup: Releasing group at `%p'.", group);
+	switch (group->type) {
+		case SGT_REAL:
+		{
+			RealSpriteGroup *rsg = &group->g.real;
+			for (i = 0; i < rsg->loading_count; i++) {
+				UnloadSpriteGroup(&rsg->loading[i]);
+			}
+			for (i = 0; i < rsg->loaded_count; i++) {
+				UnloadSpriteGroup(&rsg->loaded[i]);
+			}
+			free(group);
+			return;
+		}
+
+		case SGT_DETERMINISTIC:
+		{
+			DeterministicSpriteGroup *dsg = &group->g.determ;
+			for (i = 0; i < group->g.determ.num_ranges; i++) {
+				UnloadSpriteGroup(&dsg->ranges[i].group);
+			}
+			UnloadSpriteGroup(&dsg->default_group);
+			free(group->g.determ.ranges);
+			free(group);
+			return;
+		}
+
+		case SGT_RANDOMIZED:
+		{
+			for (i = 0; i < group->g.random.num_groups; i++) {
+				UnloadSpriteGroup(&group->g.random.groups[i]);
+			}
+			free(group->g.random.groups);
+			free(group);
+			return;
+		}
+
+		case SGT_CALLBACK:
+		case SGT_RESULT:
+			free(group);
+			return;
+	}
+
+	DEBUG(grf, 1)("Unable to remove unknown sprite group type `0x%x'.", group->type);
+}
--- a/sprite.h	Tue Oct 04 20:58:10 2005 +0000
+++ b/sprite.h	Tue Oct 04 21:42:00 2005 +0000
@@ -162,4 +162,6 @@
  * (then they are |ed to @waiting_triggers instead). */
 byte RandomizedSpriteGroupTriggeredBits(const RandomizedSpriteGroup *rsg, byte triggers, byte *waiting_triggers);
 
+void UnloadSpriteGroup(SpriteGroup **group_ptr);
+
 #endif /* SPRITE_H */