diff options
-rw-r--r-- | engine.c | 47 | ||||
-rw-r--r-- | engine.h | 3 | ||||
-rw-r--r-- | newgrf.c | 38 | ||||
-rw-r--r-- | sprite.c | 70 | ||||
-rw-r--r-- | sprite.h | 2 |
5 files changed, 157 insertions, 3 deletions
@@ -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 */ @@ -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 */ @@ -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); } @@ -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); +} @@ -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 */ |