Skip to content

Instantly share code, notes, and snippets.

@zachelko
Created April 19, 2010 04:06
Show Gist options
  • Save zachelko/370741 to your computer and use it in GitHub Desktop.
Save zachelko/370741 to your computer and use it in GitHub Desktop.
// ResourceManager.cpp
//
// Zach Elko
// 2010
//
// Functions as a garbage collector for allocating and releasing SDL resources.
//
// Supports: SDL_Surface, Mix_Music, and Mix_Chunk.
//
// It improves program efficiency by only allocating one copy for each
// resource required, and then frees the resource once all clients have
// released the resource.
//
// Could be extended using templates, but the results could range from
// negligible to detrimental because each resource needs to be
// allocated and released in their own way, so the resources would most
// likely need to be wrapped in an object with some virtual function(s)
// to handle the acquisition and release of the resources.
//
#include "ResourceManager.h"
#include "../utils/SDL_Utils.h"
#include "SDL/SDL.h"
#include "SDL/SDL_mixer.h"
#include "SDL/SDL_ttf.h"
#include <map>
#include <string>
#include <iostream>
#include <stdexcept>
// Exception class included here for brevity only
class Exception : public std::runtime_error
{
public:
Exception(const std::string& msg) : std::runtime_error(msg.c_str()) { }
};
// Initialize our static instance
// Note: Because it is static, we MUST do this in *exactly* 1 file
ResourceManager* ResourceManager::instance = 0;
SDL_Surface* ResourceManager::acquireSurface(const std::string& fileName,
const bool alpha)
{
SurfaceIterator surfaceElement = surfaces.find(fileName);
// If we've already loaded this resource, simply increment the counter
// in the tracker and return the surface
//
// Otherwise, load it and add it to the tracker with an initial count of 1
if (surfaceElement != surfaces.end())
{
TrackerIterator trackerElement = tracker.find(fileName);
++trackerElement->second;
return surfaceElement->second;
}
else
{
SDL_Surface* surface = load_image(fileName, alpha);
if (surface != 0)
{
tracker.insert(make_pair(fileName, 1));
surfaces.insert(make_pair(fileName, surface));
return surface;
}
else
{
throw Exception("Failed to load file: " + fileName);
}
}
}
void ResourceManager::releaseSurface(const std::string& fileName)
{
// If the resource already exists, check its tracker count.
// If it is currently 1, we are removing the last handle to it
// so we need to free the resource and remove it from our map.
//
// Otherwise, just decrease the count in the tracker
SurfaceIterator surfaceElement = surfaces.find(fileName);
if (surfaceElement != surfaces.end())
{
TrackerIterator trackerElement = tracker.find(fileName);
if (trackerElement->second == 1)
{
trackerElement->second = 0;
SDL_FreeSurface(surfaceElement->second);
surfaceElement->second = 0;
surfaces.erase(surfaceElement);
}
else
{
--trackerElement->second;
}
}
}
Mix_Music* ResourceManager::acquireMusic(const std::string& fileName)
{
MusicIterator musicElement = musics.find(fileName);
// If we've already loaded this resource, simply increment the counter
// in the tracker and return the surface
//
// Otherwise, load it and add it to the tracker with an initial count of 1
if (musicElement != musics.end())
{
TrackerIterator trackerElement = tracker.find(fileName);
if (trackerElement != tracker.end())
{
trackerElement->second += 1;
}
return musicElement->second;
}
else
{
Mix_Music* music = Mix_LoadMUS(fileName.c_str());
if (music != 0)
{
tracker.insert(make_pair(fileName, 1));
musics.insert(make_pair(fileName, music));
return music;
}
else
{
throw Exception("Failed to load file: " + fileName);
}
}
}
void ResourceManager::releaseMusic(const std::string& fileName)
{
// If the resource already exists, check its tracker count.
// If it is currently 1, we are removing the last handle to it
// so we need to free the resource and remove it from our map.
//
// Otherwise, just decrease the count in the tracker
TrackerIterator trackerElement = tracker.find(fileName);
if (trackerElement != tracker.end())
{
if (trackerElement->second == 1)
{
trackerElement->second = 0;
std::map<std::string, Mix_Music*>::iterator musicElement =
musics.find(fileName);
if (musicElement != musics.end())
{
Mix_FreeMusic(musicElement->second);
musics.erase(musicElement);
}
}
else
{
trackerElement->second -= 1;
}
}
}
Mix_Chunk* ResourceManager::acquireSFX(const std::string& fileName)
{
SfxIterator sfxElement = sfx.find(fileName);
// If we've already loaded this resource, simply increment the counter
// in the tracker and return the surface
//
// Otherwise, load it and add it to the tracker with an initial count of 1
if (sfxElement != sfx.end())
{
TrackerIterator trackerElement = tracker.find(fileName);
if (trackerElement != tracker.end())
{
trackerElement->second += 1;
}
// return surface
return sfxElement->second;
}
else
{
Mix_Chunk* sfxChunk = Mix_LoadWAV(fileName.c_str());
if (sfxChunk != 0)
{
tracker.insert(make_pair(fileName, 1));
sfx.insert(make_pair(fileName, sfxChunk));
return sfxChunk;
}
else
{
throw Exception("Failed to load file: " + fileName);
}
}
}
void ResourceManager::releaseSFX(const std::string& fileName)
{
// If the resource already exists, check its tracker count.
// If it is currently 1, we are removing the last handle to it
// so we need to free the resource and remove it from our map.
//
// Otherwise, just decrease the count in the tracker
TrackerIterator trackerElement = tracker.find(fileName);
if (trackerElement != tracker.end())
{
if (trackerElement->second == 1)
{
trackerElement->second = 0;
std::map<std::string, Mix_Chunk*>::iterator sfxElement =
sfx.find(fileName);
if (sfxElement != sfx.end())
{
Mix_FreeChunk(sfxElement->second);
sfx.erase(sfxElement);
}
}
else
{
trackerElement->second -= 1;
}
}
}
// ResourceManager.hpp
//
// Zach Elko
// 2010
//
// Functions as a garbage collector for allocating and releasing SDL resources.
//
// Supports: SDL_Surface, Mix_Music, and Mix_Chunk.
//
// It improves program efficiency by only allocating one copy for each
// resource required, and then frees the resource once all clients have
// released the resource.
//
// Could be extended using templates, but the results could range from
// negligible to detrimental because each resource needs to be
// allocated and released in their own way, so the resources would most
// likely need to be wrapped in an object with some virtual function(s)
// to handle the acquisition and release of the resources.
//
#ifndef RESOURCE_MANAGER_H
#define RESOURCE_MANAGER_H
#include "SDL/SDL.h"
#include "SDL/SDL_mixer.h"
#include "SDL/SDL_ttf.h"
#include <map>
#include <string>
#include <iostream>
typedef std::map<std::string, SDL_Surface*>::iterator SurfaceIterator;
typedef std::map<std::string, Mix_Music*>::iterator MusicIterator;
typedef std::map<std::string, Mix_Chunk*>::iterator SfxIterator;
typedef std::map<std::string, int>::iterator TrackerIterator;
class ResourceManager
{
public:
static ResourceManager* getInstance()
{
if (instance == 0)
{
instance = new ResourceManager;
}
return instance;
}
SDL_Surface* acquireSurface(const std::string& fileName, const bool alpha);
void releaseSurface(const std::string& fileName);
Mix_Music* acquireMusic(const std::string& fileName);
void releaseMusic(const std::string& fileName);
Mix_Chunk* acquireSFX(const std::string& fileName);
void releaseSFX(const std::string& fileName);
private:
// The instance
static ResourceManager* instance;
// Surfaces
std::map<std::string, SDL_Surface*> surfaces;
// Music
std::map<std::string, Mix_Music*> musics;
// SFX
std::map<std::string, Mix_Chunk*> sfx;
// Tracker
std::map<std::string, int> tracker;
// All of these are private due to the Singleton pattern
ResourceManager()
{
}
ResourceManager(const ResourceManager&)
{
}
~ResourceManager()
{
}
ResourceManager & operator=(const ResourceManager&)
{
}
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment