#

## Resource API Overview - Part 2

### Custom resources

It is possible to add your own resource types to clanlib, or even extend the functionality of already existing resource types.

The resource system uses an interface called CL_ResourceType to identify resource types in the resource files, and instantiate the appropiate data objects for a resource. When a resource is being loaded, the following happens:

- The resource manager parses a resource description in the resource file, and creates a CL_Resource object containing the description.
- The manager walks through a linked list of registered resource types, finding all resource types that aknowledges the resource description.
- Each resource type is asked to create and attach a resource data object (CL_ResourceData) that can handle the resource' data.
The CL_Resource object has some signals that are invoked when a resource is to be loaded, unloaded, or saved to a datafile. If a resource data object is interesting in reacting on this (a surface resource data object would load the surface into memory on a load signal), they must connect a slot to the appropiate signal.
- Each resource data object uses a identifying string that can be used to locate the object's pointer. A surface data object might register itself as "surface", and a call to CL_Resource.get_data("surface") will then return a pointer this object.

This may sound a little advanced, but a small example will show how simple it can look. First we create our data object:

    // resources.scr:
    our_map = some_location.map
    (
    	type=map,
    	size=100x200
    };

    class ResourceData_Map : public CL_ResourceData
    {
    public:
    	ResourceData_Map(CL_Resource &resource)
    	: CL_ResourceData(resource), width(0), height(0), map(NULL)
    	{
    		// Connect to the file load signal:
    		slot_load_file = resource.sig_load_file().connect(this, &ResourceData_Map::on_load_file);

    		// Attach resource data object to the resource.
    		resource.attach_data("map", this);
    	}

    	~ResourceData_Map()
    	{
    		delete[] map;
    	}

    	void on_load_file()
    	{
    		// get the size option (100x200)
    		CL_ResourceOption &size_opt = get_resource().get_options().get_option("size");

    		// get the first value of the size (100)
    		width = CL_String(size_opt.get_value(0)).get_as_int();

    		// get the second value of the size (200)
    		height = CL_String(size_opt.get_value(1)).get_as_int();

    		// allocate memory for the map
    		map = new char[width*height];

    		// open the file "some_location.map"
    		FILE *file = fopen(get_resource().get_location().c_str(), "rb");
    		fread(map, width*height, file);
    		fclose(file);
    	}

    	char *map;
    	int width, height;
    	CL_Slot slot_load_file;
    };

The data object attaches itself to the resource using the name 'map'. We can obtain a pointer to the object by using the CL_Resource::get_data() function:

    // open the resource file
    CL_ResourceManager resources("resources.scr", false);

    // get the map resource
    CL_Resource &map_resource = resources.get_resource("our_map");

    // get the attached Map data from the resource
    ResourceData_Map *map = (ResourceData_Map*) map_resource.get_data("map");

Note that the above code will not produce a call to ResourceData_Map::on_load_file(). This will first happen when a call is done to CL_Resource::load(). The same thing applies with unloading; if you loaded an object by calling load(), you must also call unload(). The resource class reference counts the loading, so several objects can call load(), but only the first call will actually perform the load.

When the resource manager parses the resource file, it will need to know what resource types exist, and how to attach data objects. In order to do this, the resource manager use a linked list of CL_ResourceType objects. It walks through all the resource type objects, and then asks wether it can attach data to the resource. If no resource type recognizes the resource, the resource manager will throw and exception, telling the program that it has encountered an unknown resource type.

ClanLib contain a template class called CL_RegisterResourceType<ResourceDataClass> that allows you to construct simple resource type objects, that just attach a single data object to the resource. Registration of resource types require that you create an instance of CL_ResourceType. The template class is inheriated from CL_ResourceType and is nothing more than a convience class. An example of its usage:

    void MyApp::main(int argc, char **argv)
    {
    	CL_SetupCore setup_core;

    	CL_RegisterResourceType<ResourceData_Map> resource_type_map("map"); // register resource type 'map'
    	CL_ResourceManager manager("resources.scr", false);
    	CL_Resource &map_resource = manager.get_resource("our_map");

    	map_resource.load(); // calls on_load_file() on our data object
    	ResourceData_Map *map = (ResourceData_Map *) map_resource.get_data("map");

    	std::cout << "width of map: " << map->width << std::endl;

    	map_resource.unload(); // calls on_unload() on our data object
    }

That's it! We have constructed our own custom resource.

### Extending existing resource types

It is possible to have several resource type objects for the same resource. This is practical if you want to extend an already existing resource type with more functionality. Each resource type object can attach its own data to the resource object, allowing you to eg. add "animation_data" to a surface type.

The following is a small example of how to extend the surface type with some additional data:

    class AnimationData;

    class AnimationSurface : public CL_Surface
    {
    public:
    	AnimationSurface(const std::string &res_id, CL_ResourceManager *resources)
    	: CL_Surface(res_id, resources)
    	{
    		CL_Resource &resource = resources->get_resource(res_id);
    		anim_data = (AnimationData *) resource.get_data("animation_data");
    	}

    	AnimationData *get_anim_data() { return anim_data; }

    private:
    	AnimationData *anim_data;
    };

    class AnimationData : public CL_ResourceData
    {
    public:
    	AnimationData(CL_Resource &resource)
    	{
    		some_data = resource.get_option("anim_data").get_value();
    		resource.attach_data(this);
    	}

    	std::string some_data;
    };

    class MyApplication : public CL_ClanApplication
    {
    public:
    	virtual int main(int argc, char **argv)
    	{
    		CL_SetupCore setup_core;
    		CL_SetupGL setup_gl;
    		CL_SetupDisplay setup_display;

    		CL_RegisterResourceType<AnimationData> restype_anim("surface");

    		CL_DisplayWindow window("Window", 640, 480);

    		CL_ResourceManager resources("resources.scr", false);
    		AnimationSurface surf("my_surf", &resources);

    		surf.draw(0, 0);

    		CL_Display::flip();

    		std::cout << "some data: " << surf.get_anim_data()->some_data.c_str() << std::endl;

    		return 0;
    	}
    } app;

Questions or comments, write to the ClanLib mailing list.