/* * resmgr.h * BoE * * Created by Celtic Minstrel on 10-08-24. * */ #ifndef BOE_RESMGR_H #define BOE_RESMGR_H #include #include #include #include #include #include #include /// A simple resource manager. /// Handles loading, retaining, and releasing of resources as necessary. /// Resources include sounds, images, fonts, and cursors. /// /// To implement a new resource type, all you have to do is specialize /// @a ResMgr::resLoader::operator() for the desired resource type. namespace ResMgr { namespace fs = boost::filesystem; /// The signature of an ID map function. using idMapFn = std::function; /// A resource pool. /// @tparam type The type of resource that this pool manages. template struct resPool { /// Get the map of all currently-loaded resources from this resource pool. static std::map >& resources() { static std::map > data; return data; } /// Get the current search path stack for this resource pool. static std::stack& resPaths() { static std::stack data; return data; } /// Get the current function used to map numerical IDs to string keys (filenames). static idMapFn& mapFn() { static idMapFn data; return data; } /// Get the map of past path resolutions. /// @return A map of relative paths to the absolute path they most recently resolved to. static std::map& pathFound() { static std::map data; return data; } /// Convert a relative path to an absolute path by checking the current search path stack. /// @param path The path to resolve. /// @return The resolved absolute path, or the relative path unchanged if resolution failed. static fs::path rel2abs(fs::path path) { std::stack tmpPaths = resPaths(); while(!tmpPaths.empty()) { fs::path thisPath = tmpPaths.top()/path; printf("Testing %s...\n",thisPath.c_str()); if(fs::exists(thisPath)) { pathFound()[path] = tmpPaths.top(); return thisPath; } tmpPaths.pop(); } // If we got this far, it wasn't found. // Just return the original filename unchanged; // maybe it can be resolved anyway. return path; } }; /// Handles all the legwork of loading a specific resource. /// Must be implemented for each resource you want to manage. /// @tparam type The type of resource. template struct resLoader { /// Load a resource of this type from the given file. /// @param fname The path to the resource; this will be an absolute path unless resolution failed. /// @return A pointer to the loaded resource. The resource manager takes responsibility for freeing it, /// so it must be a pointer allocated with `new` rather than `new[]`. type* operator() (std::string fname); }; /// Thrown if an error occurs while loading a resource. class xResMgrErr : public std::exception { std::string msg; public: xResMgrErr() throw() {} xResMgrErr(const std::string& str) throw() : msg(str) {} ~xResMgrErr() throw() {} /// @return The error message. virtual const char* what() const throw() { return msg.c_str(); } }; /// Free a single resource. /// @tparam type The type of resource to free. /// @param name The key of the resource to free (usually the filename without an extension). template void free(std::string name) { if(resPool::resources().find(name) != resPool::resources().end()) resPool::resources().erase(name); } /// Free a single resource by numerical ID. /// In order for this to work, an ID map function must have first been set with setIdMapFn(). /// @tparam type The type of resource to free. /// @param id The numerical ID of the resource to free. /// @throw std::bad_function_call if the ID map function was not set. template void free(int id) { std::string name = resPool::mapFn()(id); if(name != "") free(name); } /// Free all resources of a particular type. /// @tparam type The type of resource to free. template void freeAll() { resPool::resources().clear(); } /// Fetch a single resource, loading it into memory if necessary. /// If the resource already exists in memory, it first checks to see if the path resolution has changed, /// which could happen if a new path has been pushed on the stack, or a path has been removed. /// If it would resolve to a different file than the one currently loaded, the resource is reloaded. /// @tparam type The type of the resource to fetch. /// @param name The key of the resource to fetch (usually the filename without an extension). /// @return A smart pointer to the fetched resource. template std::shared_ptr get(std::string name) { if(resPool::resources().find(name) != resPool::resources().end()) { if(resPool::pathFound().find(name) != resPool::pathFound().end()) { std::string curPath = resPool::pathFound()[name].string(); std::string checkPath = resPool::rel2abs(name).string(); checkPath = checkPath.substr(0,curPath.length()); if(checkPath != curPath) { free(name); return get(name); } } return resPool::resources()[name]; } else { type* tmp; resLoader load; tmp = load(name); return resPool::resources()[name] = std::shared_ptr(tmp); } } /// Fetch a single resource by numerical ID. /// In order for this to work, an ID map function must have first been set with setIdMapFn(). /// @tparam type The type of the resource to fetch. /// @param id The numerical ID of the resource to fetch. /// @return A smart pointer to the fetched resource. /// @throw xResMgrErr if the ID map function returned an empty string. /// @throw std::bad_function_call if the ID map function was not set. template std::shared_ptr get(int id) { std::string name = resPool::mapFn()(id); if(name == "") throw xResMgrErr("Invalid resource ID."); return get(name); } /// Push a new path onto the path resolution stack /// @tparam type The type of resource the path applies to. /// @param path The path at which resources of this type may be found. template void pushPath(fs::path path) { printf("Pushing path %s in %s...\n",path.c_str(),__FUNCTION__); resPool::resPaths().push(path); if(resPool::resPaths().empty()) printf("A problem occurred.\n"); } /// Pop a path from the path resolution stack. /// @tparam type The type of resource the path applies to. /// @return The removed path from the top of the stack. template fs::path popPath() { fs::path path = resPool::resPaths.top(); // std::map::iterator mapiter; // std::deque toDestroy; // std::deque::iterator iter; // for(mapiter = resPool::pathFound().begin(); // mapiter != resPool::pathFound().end(); // mapiter++) // if(mapiter->second == path) // toDestroy.push_back(mapiter->first); // for(iter = toDestroy.begin(); iter != toDestroy.end(); iter++) // resPool::resources().erase(*iter); resPool::resPaths.pop(); return path; } /// Set an ID map function. /// @tparam type The type of resource for which an ID map function should be set. /// @param f The new ID map function. template void setIdMapFn(idMapFn f) { resPool::mapFn() = f; } /// Get the ID map function for a resource type. /// @tparam type The type of resource to fetch the ID map function for. /// @return The currend ID map function for this resource type. template idMapFn getIdMapFn() { return resPool::mapFn(); } } #endif