* Interface for textures, sounds, and other types of resources.
*/
-#include <cstdio>
+#include <map>
+#include <stdexcept>
#include <string>
#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+#include <moof/debug.hh>
+
namespace moof {
+class resource;
+typedef boost::shared_ptr<resource> resource_ptr;
+
+
/**
- * Generic resource class.
+ * Generic resource class capable of containing any type of resource,
+ * providing a type-safe interface.
*/
-
class resource
{
public:
- virtual ~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);
+ 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 <class T>
+ static void register_type(const std::string& extension,
+ const std::string& prefix = "")
+ {
+ loader_ptr loader(new specific_loader<T>(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 <class T>
+ 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 <class T>
+ T* get() const
+ {
+ if (check<T>()) return (T*)resource_;
+ return 0;
+ }
+
+
+ /**
+ * Deconstruct a resource container.
+ */
+ virtual ~resource();
+
+
+private:
+
+ template <class T>
+ explicit resource(T* ptr) :
+ resource_(ptr),
+ typeinfo_(const_cast<std::type_info*>(&typeid(T))),
+ unloader_(new specific_unloader<T>(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> loader_ptr;
+
+ template <class T>
+ 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> unloader_ptr;
+
+ template <class T>
+ 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 T>
+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<T>();
+ }
+
/**
- * Add directories to search when looking for resource files.
- * \param pathList The list of directory paths.
+ * Get a pointer to the underlying resource.
+ * \return The pointer, or null if this handle is not dereferenceable.
*/
- static void add_search_paths(const std::vector<std::string>& pathList);
+ T* get() const
+ {
+ if (!*this) return 0;
+ return resource_->get<T>();
+ }
+
+ /**
+ * 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<T>());
+ }
+
+ /**
+ * 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();
+ }
/**
- * 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.
- * \param prefix A colon-separated list of subdirectories to search.
- * \param extension A colon-separated list of possible extensions.
- * \return True if a path to a resource was found, false otherwise.
+ * Same a get_reference() for dereferencing the handle.
+ * \return A reference to the resource.
+ * \throws std::runtime_error If this is a null handle.
*/
- static bool find_path(std::string& path,
- const std::string& prefix = "",
- const std::string& extension = "");
+ T& operator * () const
+ {
+ return get_reference();
+ }
+
/**
- * 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 prefix A colon-separated list of subdirectories to search.
- * \param extension A colon-separated list of possible extensions.
- * \param mode The open mode.
- * \return The FILE* if the resource was found, 0 otherwise.
+ * Unload the resource associated with this handle.
*/
- static FILE* open_file(std::string& path,
- const std::string& prefix = "",
- const std::string& extension = "",
- const std::string& mode = "rb");
+ void unload()
+ {
+ resource_ = resource_ptr();
+ }
private:
- static std::vector<std::string> search_paths_;
+ 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<TYPE>(#EXT, #PREFIX); } \
+ ~EXT() { moof::resource::unregister_type(#EXT); } \
+ }; \
+ static EXT EXT; \
+}
+
+
} // namespace moof
#endif // _MOOF_RESOURCE_HH_