/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * 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_ /** * \file resource.hh * Interface for textures, sounds, and other types of resources. */ #include "../config.h" #include #include #include #include #include #include #include #include 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: // FIXME: this won't be necessary once the existing code is modified to // use the resource handles resource() {} /** * Add a directory to search when looking for resource files. * \param paths A colon-separated list of directory paths. */ static void add_search_paths(const std::string& paths); /** * Get the path to a resource of a given name. * \param path The name of the resource to find. Upon successful * return, this is changed to an absolute path to the resource. * \return True if a path to a resource was found, false otherwise. */ static bool find(const std::string& file); /** * Get the path to a resource of a given name and open it if a resource * was found. * \param path The name of the resource to find. Upon successful * return, this is changed to an absolute path to the resource. * \param mode The open mode. * \return The FILE* if the resource was found, 0 otherwise. */ static FILE* open_file(const std::string& path, const std::string& mode = "rb"); /** * 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) { if (!type_lookup_) type_lookup_ = type_lookup_ptr(new type_lookup); loader_ptr loader(new specific_loader); (*type_lookup_)[extension] = loader; } /** * 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) { type_lookup_->erase(extension); } static resource_ptr load(const std::string& path); static resource_ptr reload(std::string& path); /** * Construct a resource container. * \param ptr A pointer to the underlying resource data. */ template explicit resource(T* ptr) : resource_(ptr), typeinfo_(const_cast(&typeid(T))), unloader_(new specific_unloader(ptr)) {} /** * Deconstruct a resource container. */ virtual ~resource(); /** * Reload the resource data. This will cause the resource file to be * reread, and the underlying resource data will change. */ void reload(); /** * 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; } /** * 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(); private: class loader { public: virtual ~loader() {} virtual resource* load(const std::string& path) { return 0; } }; typedef boost::shared_ptr loader_ptr; template class specific_loader : public loader { public: 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() { log_warning("unloading resource of type ", typeid(T).name()); delete object_; } private: T* object_; }; void set_loader(const std::string& path, loader_ptr loader) { path_ = path; loader_ = loader; } void* resource_; std::type_info* typeinfo_; unloader_ptr unloader_; std::string path_; loader_ptr loader_; typedef std::map type_lookup; typedef boost::shared_ptr type_lookup_ptr; static type_lookup_ptr type_lookup_; #ifdef USE_HOTLOADING int wd_; void set_watch_descriptor(int wd) { wd_ = wd; } #endif }; /** * 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) {} /** * 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(); } private: resource_ptr resource_; }; } // namespace moof #endif // _MOOF_RESOURCE_HH_