/*] Copyright (c) 2009-2011, Charles McGarvey [***************************** **] All rights reserved. * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * *****************************************************************************/ #ifndef _MOOF_RESOURCE_HH_ #define _MOOF_RESOURCE_HH_ #include #include #include #include #include #include /** * \file resource.hh * Interface for textures, sounds, and other types of resources. */ namespace moof { class resource; typedef boost::shared_ptr resource_ptr; /** * Generic resource class capable of containing any type of resource, * providing a type-safe interface. */ class resource { public: /** * Add a directory to search when looking for resource files. * \param paths A colon-separated list of directory paths. */ static void set_search_paths(const std::string& paths); /** * Get the path to a resource of a given name. This uses the search * path(s) and resources prefixes to locate resource files. * \param name The name or partial path of the resource to find. * \return The full path of the resource. */ static std::string find_file(const std::string& name); /** * Get the path to a resource of a given name and explicit type. This * uses the search path(s) and resources prefixes to locate resource * files. * \param name The name or partial path of the resource to find. * \param ext The extension is appended to the name if the same * extension is not already a part of name. * \return The full path of the resource. */ static std::string find_file(const std::string& name, const std::string& ext); /** * Register a type with the extension of files which this type can * load. If any type has already been registered with the given file * extension, it will be replaced. * \param extension The file extension. */ template static void register_type(const std::string& extension, const std::string& prefix = "") { loader_ptr loader(new specific_loader(prefix)); call_registry(extension, loader, set); } /** * Unregister the type associated with a file extension. Resources of * this type will no longer be loadable, although resources which are * already loaded will remain loaded. * \param extension The file extension. */ static void unregister_type(const std::string& extension) { loader_ptr loader; call_registry(extension, loader, set); } /** * Find and load a resource by name or path. * \param name The name or partial path of the resource. This should * include the extension so that the correct loader can be chosen. * \return The resource. */ static resource_ptr load(const std::string& name); /** * Find and load a resource by name or path. * \param name The name or partial path of the resource. This should * include the extension so that the correct loader can be chosen. * \param * \return The resource. */ static resource_ptr load(const std::string& name, const std::string& ext); /** * Reload the resource data. This will cause the resource file to be * reread, and the underlying resource data will change. */ void reload(); /** * Get the path of file from which this resource was loaded. * \return The path. */ std::string path() const { return path_; } /** * Reloads some resources which have been modified on disk since they * were loaded. Hotloading must have been enabled at compile-time. * \return The number of resources reloaded. */ static int reload_as_needed(); /** * Get whether or not the type of the underlying resource data matches * an expected type. * \return True if the types match, false otherwise. */ template bool check() const { return *typeinfo_ == typeid(T); } /** * Get a pointer to the underlying resource data as long as the type of * the resource data matches the expected type. * \return The resource data, or null if there is a type mismatch. */ template T* get() const { if (check()) return (T*)resource_; return 0; } /** * Deconstruct a resource container. */ virtual ~resource(); private: template explicit resource(T* ptr) : resource_(ptr), typeinfo_(const_cast(&typeid(T))), unloader_(new specific_unloader(ptr)), wd_(-1) {} static resource_ptr load_with_path(const std::string& path, const std::string& extension); class loader { public: loader(const std::string& prefix) : prefix_(prefix) {} virtual ~loader() {} virtual resource* load(const std::string& path) { return 0; } const std::string& prefix() const { return prefix_; } private: std::string prefix_; }; typedef boost::shared_ptr loader_ptr; template class specific_loader : public loader { public: specific_loader(const std::string& prefix) : loader(prefix) {} virtual resource* load(const std::string& path) { return new resource(new T(path)); } }; class unloader { public: virtual ~unloader() {}; }; typedef boost::shared_ptr unloader_ptr; template class specific_unloader : public unloader { public: specific_unloader(T* object = 0) : object_(object) {} virtual ~specific_unloader() { delete object_; } private: T* object_; }; enum registry_action { lookup, set }; static bool call_registry(const std::string& extension, loader_ptr& loader, registry_action action); void* resource_; std::type_info* typeinfo_; unloader_ptr unloader_; int wd_; std::string path_; std::string type_; }; /** * The resource handle class provides a nicer way to work with resources. * It allows you to work with a resource pointer as if you already know the * type of the resource. */ template class resource_handle { public: /** * Construct a null resource handle. */ resource_handle() {} /** * Construct a resource handle. * \param ptr The resource pointer to reference. */ resource_handle(resource_ptr ptr) : resource_(ptr) {} explicit resource_handle(const std::string& name) : resource_(resource::load(name)) {} resource_handle(const std::string& name, const std::string& ext) : resource_(resource::load(name, ext)) {} /** * Get whether or not the handle is dereferenceable to the type of this * handle. A resource handle is dereferenceable if it is not a null * handle and if its underlying resource is in fact the same type as is * expected by the handle. * \return True if the handle is dereferenceable, false otherwise. */ operator bool () const { if (!resource_) return false; return resource_->check(); } /** * Get a pointer to the underlying resource. * \return The pointer, or null if this handle is not dereferenceable. */ T* get() const { if (!*this) return 0; return resource_->get(); } /** * Dereference the handle all the way to the underlying resource. * \return A reference to the resource. * \throws std::runtime_error If this is a null handle. */ T& get_reference() const { if (!*this) throw std::runtime_error("dereference null handle"); return *(resource_->get()); } /** * Same as get() for getting a pointer to the underlying resources. * \return The pointer, or null if this handle is not dereferenceable. */ T* operator -> () const { return get(); } /** * Same a get_reference() for dereferencing the handle. * \return A reference to the resource. * \throws std::runtime_error If this is a null handle. */ T& operator * () const { return get_reference(); } /** * Unload the resource associated with this handle. */ void unload() { resource_ = resource_ptr(); } private: resource_ptr resource_; }; /** * This macro easily registers types to act as resources. It should be * used in a module file in global scope. * \param TYPE The type (class), qualified as needed for the scope. * \param EXT The file extension the resource uses. * \param PREFIX The path prefix where a resource of this type could be. */ #define MOOF_REGISTER_RESOURCE(TYPE, EXT, PREFIX) \ namespace { \ struct EXT { \ EXT() { moof::resource::register_type(#EXT, #PREFIX); } \ ~EXT() { moof::resource::unregister_type(#EXT); } \ }; \ static EXT EXT; \ } } // namespace moof #endif // _MOOF_RESOURCE_HH_