summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine.c47
-rw-r--r--engine.h3
-rw-r--r--newgrf.c38
-rw-r--r--sprite.c70
-rw-r--r--sprite.h2
5 files changed, 157 insertions, 3 deletions
diff --git a/engine.c b/engine.c
index 2668e9271..b2f369d01 100644
--- a/engine.c
+++ b/engine.c
@@ -294,6 +294,28 @@ static const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, byte overri
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 @@ static SpriteGroup *engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID];
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 */
diff --git a/engine.h b/engine.h
index f8bdbe0ee..4fbb3aa33 100644
--- a/engine.h
+++ b/engine.h
@@ -276,4 +276,7 @@ static inline const RoadVehicleInfo* RoadVehInfo(EngineID e)
return &_road_vehicle_info[e - ROAD_ENGINES_INDEX];
}
+void UnloadWagonOverrides(void);
+void UnloadCustomEngineSprites(void);
+
#endif /* ENGINE_H */
diff --git a/newgrf.c b/newgrf.c
index 9bbeea5c2..fb8f28a74 100644
--- a/newgrf.c
+++ b/newgrf.c
@@ -1404,6 +1404,8 @@ static void NewSpriteGroup(byte *buf, int len)
}
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 @@ static void NewSpriteGroup(byte *buf, int len)
}
}
+ if (_cur_grffile->spritegroups[setid] != NULL)
+ UnloadSpriteGroup(&_cur_grffile->spritegroups[setid]);
_cur_grffile->spritegroups[setid] = group;
group->ref_count++;
return;
@@ -1519,6 +1523,8 @@ static void NewSpriteGroup(byte *buf, int len)
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 @@ static void InitializeGRFSpecial(void)
}
/**
+ * 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 @@ static void ResetNewGRFData(void)
}
}
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 @@ static void LoadNewGRFFile(const char* filename, uint file_index, uint stage)
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);
}
diff --git a/sprite.c b/sprite.c
index a1cf5b278..28c430f32 100644
--- a/sprite.c
+++ b/sprite.c
@@ -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 @@ byte RandomizedSpriteGroupTriggeredBits(const RandomizedSpriteGroup *rsg,
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);
+}
diff --git a/sprite.h b/sprite.h
index 785a6af1a..45f14a271 100644
--- a/sprite.h
+++ b/sprite.h
@@ -162,4 +162,6 @@ SpriteGroup *EvalRandomizedSpriteGroup(const RandomizedSpriteGroup *rsg, byte ra
* (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 */