From ea435825b74af74c4606a86a236258e7055bb068 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Sat, 6 Dec 2014 18:02:41 -0500 Subject: [PATCH] Some basic documentation of the resource manager. It could probably be improved a lot. --- osx/doxy/Doxyfile | 3 +- osx/tools/resmgr/resmgr.hpp | 64 +++++++++++++++++++++++++++++++++-- osx/tools/resmgr/restypes.hpp | 9 +++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/osx/doxy/Doxyfile b/osx/doxy/Doxyfile index cdb5261f..566e9573 100644 --- a/osx/doxy/Doxyfile +++ b/osx/doxy/Doxyfile @@ -765,7 +765,8 @@ WARN_LOGFILE = doxy_warnings.txt # Note: If this tag is empty the current directory is searched. INPUT = ../dialogxml \ - mainpage.md + mainpage.md \ + ../tools/resmgr # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/osx/tools/resmgr/resmgr.hpp b/osx/tools/resmgr/resmgr.hpp index e13470a7..64b06dc6 100644 --- a/osx/tools/resmgr/resmgr.hpp +++ b/osx/tools/resmgr/resmgr.hpp @@ -17,29 +17,44 @@ #include #include -// Will handle loading, retaining, and releasing of resources. -// Resources include sounds, images, fonts, and cursors. +/// 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()) { @@ -58,35 +73,61 @@ namespace ResMgr { } }; + /// 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()) { @@ -107,18 +148,31 @@ namespace ResMgr { } } + /// 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; @@ -135,10 +189,16 @@ namespace ResMgr { 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(); } diff --git a/osx/tools/resmgr/restypes.hpp b/osx/tools/resmgr/restypes.hpp index dca57431..8d6b2c51 100644 --- a/osx/tools/resmgr/restypes.hpp +++ b/osx/tools/resmgr/restypes.hpp @@ -28,6 +28,7 @@ using StringRsrc = std::vector; using SoundRsrc = sf::SoundBuffer; namespace ResMgr { + /// Load an image from a PNG file. template<> inline ImageRsrc* resLoader::operator() (std::string fname) { fs::path fpath = resPool::rel2abs(fname + ".png"); ImageRsrc* img = new ImageRsrc(); @@ -36,6 +37,9 @@ namespace ResMgr { throw xResMgrErr("Failed to load PNG image: " + fpath.string()); } + /// Load a cursor from a GIF file. + /// The cursor's hotspot location is stored in a .hot file, as plaintext: + /// simply the X value followed by the Y value, separated by whitespace. template<> inline CursorRsrc* resLoader::operator() (std::string fname) { // TODO: Store the hotspots on disk instead of hardcoded here static const std::map cursor_hs = { @@ -70,6 +74,7 @@ namespace ResMgr { throw xResMgrErr("Failed to load GIF cursor: " + fname); } + /// Load a font form a TTF file. template<> inline FontRsrc* resLoader::operator() (std::string fname) { fs::path fpath = resPool::rel2abs(fname + ".ttf"); FontRsrc* theFont = new FontRsrc; @@ -80,6 +85,9 @@ namespace ResMgr { throw xResMgrErr("Failed to find font: " + fpath.string()); } + /// Load a list of strings from a TXT file. + /// Each line in the file becomes one string in the resulting list. + /// (Empty lines are included too.) template<> inline StringRsrc* resLoader::operator() (std::string fname) { fs::path fpath = resPool::rel2abs(fname + ".txt"); std::ifstream fin(fpath.c_str()); @@ -96,6 +104,7 @@ namespace ResMgr { return strlist; } + /// Load a sound from a WAV file. template<> inline SoundRsrc* resLoader::operator() (std::string fname) { fs::path fpath = resPool::rel2abs(fname + ".wav"); SoundRsrc* snd = new SoundRsrc;